Categories
Software Development

WordPress Blocks: Hydrate InnerBlocks From HTML

Quick tip about creating or replacing InnerBlocks from HTML in the WordPress block editor. For instance if your block pulls information from a remote API and you want to display as part of the post, or embed in the post such that it’s editable. The block editor (gutenberg) already handles the job very simply.

Use pasteHandler. It not only includes HTML support, but plain text & markdown also!

import { pasteHandler } from '@wordpress/blocks';

And from a dispatch of the block-editor store, use the replaceInnerBlocks() method.

I had a couple issues with the code that made it somewhat confusing.

  1. I’m still getting used to the concept of React hooks, so managing the useSelect, useDispatch, and useCallback might not be 100% correct — but this incantation worked for me 😆.
  2. Originally I tried updating the first InnerBlock and finding it was slightly tricky. Good news — this is actually fairly simple with useSelect to getBlocksByClientId() and then just get the innerBlocks property of the first block returned.

InnerBlocks Example:

// `clientId` is passed as a prop to the edit component.
const { replaceInnerBlocks } = useDispatch( 'core/block-editor' );

// call `updateInnerBlocks( markdownOrHTMLContent );`
const updateInnerBlocks = useCallback(
	source => {
		// Get an array of inner blocks
		const newInnerBlocks = pasteHandler( {
			HTML: '',
			mode: 'BLOCKS',
			plainText: source,
		} );
		replaceInnerBlocks( clientId, newInnerBlocks );
	},
	[ innerBlocks ]
);

Interested in more? See other posts about WordPress and the block editor.

Categories
Software Development

WordPress Blocks: React Context Cuts Clutter

Using Reacts’ Context in WordPress blocks passes parameters painlessly. Attributes carried by context keep coupling minimal.

Reacts’ Context feature facilitates distributing data down a component tree to any descendant component. Instead of passing attributes between your components manually (e.g. <FooComponent attrib={ attrib }>), which tightly couples the current attributes and components, using a block-level context makes it easy to update and manage attributes (and their changes).

WordPress Blocks (a.k.a. Gutenberg) supports a similar concept called Block Context, but it’s for communicating and sharing attributes between discrete blocks (those registered using registerBlockType).

To someone familiar with React the following is probably obvious and straight-forward, but I found implementing it non-trivial, and also not covered in the WordPress documentation. Regardless, this appears to be so functional it should be a best practice.

Example:

A WordPress block has at minimum 2 top-level components, an Edit and a Save. Each component is passed the block attributes in the first argument. Typically the save component is less-interesting as it specifies the block data saved in the post-content, but if required it would function identically to the following example used with the edit component.

For this example let’s examine a block with 1 attribute, passed to both the toolbar and the sidebar.

export default function Edit( props ) {
	const { foo = '' } = props.attributes;
	const blockProps = useBlockProps();

	const updateFoo = useCallback(
		( _foo ) => {
			props.setAttributes( { foo: _foo } );
		},
		[ foo ]
	);

	return (
		<div { ...blockProps }>
			<MyPluginBlockControls foo={foo} onFooChange={updateFoo}></MyPluginBlockControls>
			<MyPluginInspectorControls foo={foo} onFooChange={updateFoo}></MyPluginInspectorControls>
			...
		</div>
	);
}

While this example is a bit contrived (with only one attribute it is probably not necessary to create subcomponents managing our controls), it should be evident that adding additional attributes cascades changes through the tree. Using a Context instead would resemble code like (differences are highlighted):

import { FooContext } from './context';

export default function Edit( props ) {
	const { foo = '' } = props.attributes;
	const blockProps = useBlockProps();

	const updateFoo = useCallback(
		( _foo ) => {
			props.setAttributes( { foo: _foo } );
		},
		[ foo ]
	);

	const blockContext = {
		foo,
		updateFoo,
	};

	return (
		<FooContext.Provider value={ blockContext }>
			<div { ...blockProps }>
				<MyPluginBlockControls></MyPluginBlockControls>
				<MyPluginInspectorControls></MyPluginInspectorControls>
				...
			</div>
		<FooContext.Provider/>
	);
}

The Context

Creating the context itself is pretty simple:

import { createContext, useContext } from '@wordpress/element';

export const FooContext = createContext( {
	foo: undefined,
	updateFoo: () => {},
} );

export const useFooContext = () => useContext( FooContext );

Using the Context in a sub-component

import { useFooContext } from './context';

export function MyPluginBlockControls() {
	// Use `foo` as normal.  If you need to write it, you can also add `updateFoo`.
	const { foo } = useFooContext();
}

Adding additional attributes

If you modify your block.json to have additional attributes it’s simple to add them to the context (use the property attribute and create a write function at the top-level for every attribute ), then it’s trivial to change the components to import the attribute with the const {foo,bar} = useFooContext() syntax.

Conclusion

This pattern appears to be a good way to decouple attributes within a WordPress block that need to be passed either to re-usable components or just to simplify what code should change when adding/updating the attributes being used. I’m still a React novice and I would love to know if there’s a problem or if it could be improved any way — let me know in the comments!

Categories
Software Development

Debugging Gutenberg Blocks: 1

I’m adding some tags for tips as I find them.

I found this tip helpful when debugging what the WordPress Block Editor (Gutenberg) was actually doing behind the scenes.

Find the post id (in the edit url there will be a field for post (e.g. post.php?post=121&action=edit). Using the WP-CLI tool you can quickly view the source of the post (assuming you have a very basic post with just the block you are developing) by running:

wp post get 121 --field=post_content

This will display the content showing the HTML as it is stored in the database — including the attributes for the block!