humanmade / hm-gutenberg-tools

Useful helpers, components or tools for building things with Gutenberg

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Question on your article

robbisy opened this issue · comments

Hi Matthew I've read your article 'Gutenberg on Humanmade.com' and I was impressed by the work you've done here. I have one question about the stat block I've seen in the video, there is 'add new stat' button and close one on each stat. Do you reuse gutenberg component for that or do you create your own one with react state ?
I find it very useful to give the possibity to add or remove component in gutenberg but so far in gutenberg I just find the delete block option.
I don't know gutenberg very well so maybe I'm wrong

Yes this is a custom block. Gutenberg is pretty new so there's no really many established patterns. This one seems pretty simple though and works well. Here's some more info:

  • The block has a property stats which is an array.
  • Each item in the stats array looks something like this { figure: '100k', text: humans }.
  • The button "add new stat" just appends a new stat object to this array and calls setAttributes
  • The remove button just removes the item at that index.
export default {
	title: 'HM Stats',
	icon:  'chart-line',

	attributes: {
		stats: {
			type:    'array',
			default: [],
		},
	},

	edit( { attributes, setAttributes, focus } ) {
		const { stats, block_bg } = attributes;

		const updateStatProp = ( index, prop, value ) => {
			const newStats = _cloneDeep( stats );
			newStats[ index ][ prop ] = value;
			setAttributes( { stats: newStats } );
		}

		return <div className={ classNames( 'pb-module', 'pb-stats', `pb-module--${block_bg}` ) } key="block">
			<div className="container">
				{ ( stats.length > 0 ) && <div className="base-wrap pb-stats__container">
					{ stats.map( ( { figure = '', text = '' }, i ) => {
						return <div className="pb-stats__stat" key={ i }>
							<Editable
								className="pb-stats__num"
								placeholder="1000k!!"
								value={ figure }
								onChange={ value => updateStatProp( i, 'figure', value[0] ) }
							/>
							<Editable
								className="pb-stats__desc"
								placeholder="Some statistic."
								value={ text }
								onChange={ value => updateStatProp( i, 'text', value[0] ) }
							/>
							<Button
								isLarge={ true }
								onClick={ () => {
									const newStats = _cloneDeep( stats );
									newStats.splice( i, 1 );
									setAttributes( { stats: newStats } );
								} }><Dashicon icon="no-alt" /></Button>
						</div>
					} ) }
				</div> }
				<div style={ { textAlign: 'center', padding: '8px 0' } }>
					{ ( stats.length < 5 ) && <Button isLarge={ true } onClick={ () => {
						const newStats = _cloneDeep( stats );
						newStats.push( { figure: '', text: '' } );
						setAttributes( { stats: newStats } );
					} }>Add new stat</Button> }
				</div>
			</div>
		</div>
	},
}

why do you use _ before cloneDeep( stats ).

Ah I didn't include the imports at the top of the file. But there are a few different ways, you can import this function from lodash.

  • import lodash as _ at the top of your file like this: import _ from 'lodash', and then you can just do this: _.cloneDeep
  • Import a single function from lodash. You can call the function whatever you like, I just prefixed all lodash functions with an _ but its not neccessary. Do it like this :import cloneDeep from lodash/cloneDeep. Then you just use your _cloneDeep function exactly the same as you would _.cloneDeep

Why one vs. the other?

The first is probably simplest, but imports the whole of lodash. The second, only imports the single function (and any dependencies).

In the past, I believe doing the 2nd one kept file sizes a little smaller. But tools have got really smart though now, and your webpack build process should strip all the unnecessary code when you're creating a production build - so long as you've configured it correctly! Which I'm really not sure I have, but I've already spent long enough messing around with webpack configs!

Thanks for your explanations Matthew 💯