When you start building web applications with React, you begin to realize that you are often passing your application’s state (or portions of the state) down to child components. Other times you are lifting state up to a common ancestor component for the child components to share. A solution to centralizing the application state is by using Redux which brings a lot of great features to make working with your application’s state easier.
The WordPress Data Module, like Redux, is used to manage application state for both plugins and WordPress. The data module is implemented on top of Redux but provides a way to create separate stores. A store is an object that holds the application’s state. You can read more about how the WordPress Data Module differs from Redux in the Gutenberg Handbook.
Gutenberg registers a handful of stores for individual modules within the application’s state by default. You can also view the reference for these data modules in the Gutenberg Handbook. We are going to build an example block that utilizing the selectors and actions of the core/editor
and core/notices
data modules in this walk-through.
What is a selector?
Selectors are the primary mechanism for retrieving data from the application state. For example, if we wanted to retrieve a list of all blocks from this post we would call the getBlocks()
selector from the core/editor
data store.
Here’s how to do it in your browser’s developer console within the Gutenberg editor.
wp.data.select('core/editor').getBlocks();
We would receive the following array containing all the block data of this post.
[
{ "clientId": "a8f90fd9-3c96-4226-b578-1a53a5adc836", "name": "core/paragraph", ... },
{ "clientId": "34208a79-7da9-4d07-89cb-3a3a54d5f7a0", "name": "core/paragraph", ... },
{ "clientId": "967498a2-1055-48e5-8d09-71bc1a40ff80", "name": "core/paragraph", ... },
{ "clientId": "bffa3517-0ca3-42be-b70d-2399c7007a57", "name": "core/heading", ... },
{ "clientId": "5113943b-75dd-4793-8ae5-2752f28955e3", "name": "core/paragraph", ... },
{ "clientId": "f692fc2f-6092-42a4-9f37-68bacd3c18ff", "name": "core/code", ... },
{ "clientId": "2f85326b-3b1c-4cfe-b328-851435043c17", "name": "core/paragraph", ... },
{ "clientId": "cf1702ea-2f08-4f01-be86-19ece86ee45a", "name": "core/code", ... },
{ "clientId": "0c09eefd-0cec-4552-be05-1aadcf0609a7", "name": "core/heading", ... },
{ "clientId": "181b4b26-b6f8-4c00-bd8e-2f1b7806fe3d", "name": "core/paragraph", ... }
]
What is an action
Actions are the primary mechanism for making changes to the application state. For this example, we can create a new notice by dispatching the createNotice()
action from the core/editor
data store.
Here’s how to do it in your browser’s developer console within the Gutenberg editor.
wp.data.dispatch('core/notices').createNotice( 'success', 'Here is our notice!' );
We would then see the following notice displayed directly below the editor’s top bar.
Setting up our block
In Getting Started with Block Development in ES.Next, we created a simple block with a minimal build process using webpack and Babel. In How to Internationalize Your Block, we updated our simple block with internationalization and did a little refactoring so let’s start from that. You can also grab the code from GitHub.
We need to update the wp_register_script
call to include the wp-data
and wp-compose
dependencies.
Create our block
We’re going to put together a simple block that will display the block types in the current post and include buttons to trigger different notification types. This should give you a good starting point to explore other data stores and select or dispatch the available actions.
Let’s get started by creating a functional component, which we’ll use in our edit
function, and importing our dependencies.
There are a few new functions and components we’re importing to work with the data stores: compose
, withSelect
, and withDispatch
.
withSelect
This is a higher-order component that we use to pass data as props from the selected data store to our component.
Read more in the Gutenberg Handbook
withDispatch
This is a higher-order component that we use to pass dispatch functions as props from the selected data store to our component.
Read more in the Gutenberg Handbook
compose
This function composes multiple higher-order components into a single higher-order component. We will pass our withSelect
and withDispatch
function calls as parameters of compose
alongside our component. Our component will then have access to the selectors and actions we define.
Read more in the Gutenberg Handbook
Write the component
Next we’ve created a component named MyComponentBase
which accepts our props and renders the output of our block edit
function. I’ve used variable deconstruction in the received parameters to select what we’re going to use from the props passed in. This is another cool feature of ES.Next that I love using.
We’re also creating a function to easily dispatch the createNotice
action. In the returned JSX, we’re calling this function whenever one of the links are clicked.
And finally we’re creating an unordered list by looping through all the blocks passed and outputting their name.
Now that we have our component ready, it’s time to pass in our selectors and actions. Using withSelect
and withDispatch
we need to return an object which will be mapped to our component as props. We need one selector to retrieve all the blocks in the post and one action to dispatch our notifications.
Note that I am using another shorthand available in ES.Next to deconstruct variables from the returned dispatch('core/notices')
call and at the same time returning an object containing it. This would be the equivalent:
We can get a list of all blocks in the post with the getBlocks
selector of the core/editor
data store. In our withSelect
call we are passing the result under the blocks
property. That way we can use props.blocks
in our component to work with them. What is really cool is that our component will update with any blocks added or removed during editing!
We can dispatch a notification to the editor with the createNotice
action of the core/notices
data store. In our withDispatch
call we are passing the action dispatcher under the createNotice
property. We can then use props.createNotice()
to dispatch the notice within our component.
Finally, let’s put it all together with compose
and update our edit
function within the registerBlockType
call.
Activate the plugin and add the block to a post to see it in action! You should see something like this:
Conclusion
Hopefully this quick walk-through gives you a simple overview of how to interact with the data stores in Gutenberg. You should be able to explore the existing selectors and actions of the core data stores and interact with them in a similar way. In a future post we’ll tackle creating our own data stores with selectors and actions.
I’ve put all the code for this walk-through up on GitHub for reference.