use() is one of the more obscure functions from the @wordpress/data package, mainly because it lacks proper documentation and examples illustrating its usage. By “lacking” I mean the official documentation only has a cryptic two-liner definition for it:

Extends a registry to inherit functionality provided by a given plugin. A plugin is an object with properties aligning to that of a registry, merged to extend the default registry behavior.

To unpack this a little, let’s first understand the following terms – registry and plugin.

Registry

In the context of the @wordpress/data package, a registry refers to an object containing all the registered namespaced stores, their states as well as some APIs like dispatch and select. The full list of properties can be found here.

Plugin

In the source code for use(), we can see that a plugin is a function that receives registry as its first parameter and returns an object. The returned object should contain the same properties as registry as those will then be merged.

This means we can use use() to extend the registry and add our own functionality to properties like the dispatch function.

Use case

Now that we know we can extend the default dispatch function from the registry using use(), let’s see what we can do with it.

Track insertion of block pattern

Keeping tabs on when particular block patterns are added to the editor can be valuable. This allows us to monitor their frequency of use or implement actions such as displaying notifications. In the following example, we’ll look at how to accomplish the latter.

We’ll start by creating a function that takes in a registry object as its first parameter and returns an object with the dispatch function. In the source code for the original dispatch function, we can see that it accepts a storeDescriptor parameter so this is something we should include as well:

const plugin = ( registry ) => {
	return {
		dispatch( storeDescriptor ) {
			// rest of the code...
		},
	};
};

When inserting a block pattern, the action creator involved is insertBlocks and this comes from the core/block-editor store. Because almost every action within the editor triggers dispatch, we want to ensure our logic only runs when we’re working with a specific store. What we can do next is to use storeDescriptor to check what store we’re working with and exit early if it’s not core/block-editor (L4-9):

const plugin = ( registry ) => {
	return {
		dispatch( storeDescriptor ) {
			const storeName = typeof storeDescriptor === 'object' ? storeDescriptor.name : storeDescriptor;

			if ( 'core/block-editor' !== storeName ) {
				// Since we're only targeting the core/block-editor store, we want to ensure the default dispatch function is returned for every other store.
				return registry.dispatch( storeName );
			}
		},
	};
};

The next step (L12-19) is to retrieve all action creators for core/block-editor. This can be achieved by calling dispatch(), which returns an object of all the actions from a store. We also want to store the original insertBlocks action creator in a variable for later use (explained below) :

const plugin = ( registry ) => {
	return {
		dispatch( storeDescriptor ) {
			const storeName = typeof storeDescriptor === 'object' ? storeDescriptor.name : storeDescriptor;
			console.log( storeName );

			if ( 'core/block-editor' !== storeName ) {
				// Exit early if the store is not the block editor.
				return registry.dispatch( storeName );
			}

			// Retrieve all action creators from the core/block-editor store. Ensure we don't mutate the original object.
			const actions = { ...registry.dispatch( storeName ) };
			const originalAction = actions?.insertBlocks;

			if ( ! originalAction ) {
				// Exit early if the store does not have the insertBlocks action.
				return actions;
			}
		},
	};
};

Lastly, we’ll extend the insertBlocks action creator by redeclaring it as a function that calls our custom function and returning its original behavior which we have stored in the originalAction variable. This is required as we do not want to completely override its default behavior but extend it (L20-25):

const plugin = ( registry ) => {
	return {
		dispatch( storeDescriptor ) {
			const storeName = typeof storeDescriptor === 'object' ? storeDescriptor.name : storeDescriptor;

			if ( 'core/block-editor' !== storeName ) {
				// Exit early if the store is not the block editor.
				return registry.dispatch( storeName );
			}

			// Retrieve all action creators from the core/block-editor store.
			const actions = { ...registry.dispatch( storeName ) };
			const originalAction = actions?.insertBlocks;

			if ( ! originalAction ) {
				// Exit early if the store does not have the insertBlocks action.
				return actions;
			}

			actions.insertBlocks = ( ...args ) => {
                                // run our custom function
				trackBlockInsertion( ...args );
				return originalAction( ...args );
			};

			return actions;
		},
	};
};

That’s it for our plugin which we’ll be passing to use() as its first parameter. All that’s left is to create the trackBlockInsertion function:

const trackBlockInsertion = ( blocks, ...args ) => {
   /**
    * The object/string containing the pattern name is the last argument.
    * See https://github.com/WordPress/gutenberg/blob/trunk/docs/reference-guides/data/data-core-block-editor.md#insertblocks
    */
	const meta = args[ args.length - 1 ];
	/**
	 * When pattern is inserted via the block editor pattern list, meta will be the pattern name.
	 * When pattern is inserted via the block inserter search, meta will be an object with a patternName prop.
	 */
	const patternName = meta?.patternName || meta;
    const patternToTrack = 'edcwm/social-links'

	if ( patternName && patternName === patternToTrack) {
		dispatch( 'core/notices' ).createSuccessNotice( __( 'Thank you for using this awesome pattern 🔥!', 'edcwm' ), {
			type: 'snackbar', 
		} );
	}
};

Leave a Reply

Your email address will not be published. Required fields are marked *