In this final part of the Gutenberg custom block tutorial series we’ll learn how to use higher-order components to utilize WordPress’ components for performing queries for posts and other core WordPress information.
In the previous part we learned about dynamic blocks and we ended up implementing functionality for typing in a post ID and using PHP to dynamically fetch the post and render it in frontend and preview mode. Manually typing in a post ID is not intuitive or user-friendly. It’s much better to provide the user some way to select or search for posts by post title and clicking onto something to choose one.
One part of solving this is fairly easy; how to query posts from your block’s edit
function. We have a few options for this, and the best option is to use some of the so-called higher-order components from WordPress. You can also use Javascript browser methods to perform an AJAX call towards WordPress REST API using for example fetch
or axios. WordPress actually provides its own version of fetch
: apiFetch()
.
The other part of solving this is a bit up to you; which is how to present the list or choice in our block. How are you going to present the list of posts to choose from? In a select, list of checkboxes or radio buttons? Or do you want to offer the possibility to search, and thus implement an autocomplete solution or a filter solution? Should you allow selecting multiple posts or just one? Usually you can solve this by using different WordPress components, but you need to decide which solution you want to implement.
Let’s first learn a bit about higher-order components and the WordPress data module, before we look at how we can perform post queries in our block.
WordPress Core Data module and higher-order components
When working with React you’ll often need to pass state down to child components or upwards to a common parenting component so that all other child components have access to them. A solution to solve the issue of centralizing an application’s state is by using Redux. With Redux you can build stores – which are objects that hold an application’s state and information.
The WordPress Data module is a hub of different stores and provides functions for managing data between different modules. It’s built on top of Redux – but don’t mistake it as a Redux for WordPress, as there are quite a few differences. You can register your own stores within WordPress, or perhaps more importantly, access WordPress’ registered stores.
Here’s an overview of the available stores in the WordPress data module (will probably change over time). All WordPress’ stores are contained in the Core Data module. For example there are stores holding the editor’s data (core/editor
), notices (core/notices
), block data (core/blocks
), viewport information (core/viewport
), and last but not least the main store itself – core
.
In order to access data from stores you’ll need to use selectors. WordPress has a selector within the wp.data
package; select()
. You can also manipulate the stores with dispatch
, but this is not covered by this tutorial series. You can actually really easily try the selector out yourself to see what is available within WordPress’ stores.
Trying out the selector
Open up the Gutenberg editor in Chrome, and open up the Console debugger tool. Type in:
wp.data.select('core')
And press Enter. You should get an object as response with all selectors (functions) you can use. As examples you’ll find functions like getMedia
, getTaxonomy
, getAuthors
, and so on. The one we’ll use to query posts is also there but doesn’t have an intuitive name; it’s called getEntityRecords
. At the moment some of these functions are documented, but most are not, unfortunately.
Also try other stores than core
, for example:
wp.data.select('core/editor').getBlocks()
This returns all information about all blocks currently in your post. You can play around with this in Chrome console debugger and try to call some functions to see what you get in response. Some requires parameters and others not.
In order to use selectors and access stores, we need to use them within higher-order components. Higher-order components are simply a pattern of doing something in React. We pass a component to a function (or component) which could adds some props, and then return a new component.
Inside WordPress Data module we find withSelect
; a higher-order component that can be used to inject props using registered selectors. In other words; within withSelect
we have access to the selector select()
and can use it to perform call(s). The selector results will be props to the component we pass to withSelect
. If you need to combine multiple higher-order components WordPress Data module offers the compose
function, but this is outside the scope of this tutorial. We will only use one higher-order component; withSelect
.
This has been a lot of theory, so let’s start looking at some code and practical examples.
Fetching posts using withSelect, select and getEntityRecords
To summarize the above, we need to set up the higher-order component withSelect
for our block. Inside this we can use selectors to access WordPress’ stores, which will be props to the component we pass to withSelect
. We will use the core
store and the selector getEntityRecords
to query posts.
The function getEntityRecords
is unfortunately not very documented at the moment. But I’ve learned that we can pass postType
as first parameter (entity kind) and then the post type as second parameter (e.g. ‘post
‘ or ‘page
‘). The third parameter is optional and can be an object with query arguments. We’ll look at the third parameter later on.
If you followed this tutorial series from the previous part, you would have a custom block that accepts a manually typed in post ID in a text input. The block uses PHP to dynamically render the post in frontend (and in preview mode). Let’s remove the requirement of manually typing in post ID and replace it with something more intuitive. As mentioned before you need to decide for yourself how to present the list of posts and the best way to let the user choose a post. To keep it simple we’ll add a select of all post titles to choose from.
Coding the withSelect
Let’s start coding this in. First we need to destructure what we need from the data package;
const { withSelect, select } = wp.data;
Then we use withSelect
in our block’s edit
function and pass on our edit component; FirstBlockEdit
. Inside withSelect
we destructure select
as parameter and use the selector select()
to query posts with getEntityRecords
. We return an object with one property which we calls posts
that holds the result of the select()
call.
...
edit: withSelect(select => {
return {
posts: select('core').getEntityRecords('postType', 'post')
}
})(FirstBlockEdit),
save: () => { return null }
...
With the code above our component, FirstBlockEdit
, will now have a new prop; posts
. Whatever we return inside the withSelect
higher-order component will be accessible as props to the component we pass (in the paranthesis at the very end).
Handling the posts from the selector
We can now go into our component FirstBlockEdit
and take a look at the new props.posts
. Because our component is a class-based component we need to refer to props with this
. Let’s console log it out inside the render()
function in FirstBlockEdit
:
render() {
const { attributes, setAttributes } = this.props;
console.log(this.props.posts);
...
}
Keep an eye on your console debugger. You might notice that this will log twice; first null
, and then sometime later it logs an array of posts. This is because querying posts is done async. Our component is first rendered before the response, at which time props.posts
is null
. Once we get a response, our component is re-rendered again with the prop populated. You should always remember to accomodate for this small timefrime without data in your code.
Adding a select to display the posts
Let’s prepare to populate a select with the posts returned and for that we’ll use WordPress component SelectControl
. The component SelectControl
accepts an array of choices where each choice is an object with the properties value
and label
.
If you take a look at the console logged (second) response you can see that we get an array of post objects. Each post contains most of the post’s information, but for the choices in a select we are only interested in the post ID as value and post title as label. So we’ll loop through the posts
prop and populate an array variable that we’ll pass on to SelectControl
. Don’t forget to handle the small timeframe where the posts
prop is null
. In that case we’ll populate the choice array with one option that has the label “Loading…”.
let choices = []; if (this.props.posts) { choices.push({ value: 0, label: __('Select a post', 'awhitepixel') }); this.props.posts.forEach(post => { choices.push({ value: post.id, label: post.title.rendered }); }); } else { choices.push({ value: 0, label: __('Loading...', 'awhitepixel') }) }
Note that we need to refer the post title as post.title.rendered
. You can look for yourself in the console logged posts
and see how the information is structured for each post.
After this we simply need to add a SelectControl
wherever we want it. It can be inside the block itself (preferably within the code for edit mode), or inside Inspector.
<SelectControl label={__('Selected Post', 'awhitepixel')} options={choices} value={attributes.selectedPostId} onChange={(newval) => setAttributes({ selectedPostId: parseInt(newval) })} />
We set the SelectControl
to refer to the attribute selectedPostId
which we defined in the previous step. We set the saved value in the prop value
and handle updating it in the onChange
prop – just like we’ve done several times before. We ensure that a number is stored by using parseInt()
because the selectedPostId
has the type number
. And we pass the generated array of choices in the prop options
.
That’s really all to it! If you followed the code from the previous step you should already have code that reads the saved post ID and displays it!
Of course I recommend that you implement the list and choice of posts differently than just a simple select. This is not a pretty or user-friendly solution, especially for sites with a lot of posts. Speaking of the number of posts, did you notice that the selector getEntityRecords only returns a maximum of the 10 latest posts? That’s the default behaviour of getEntityRecords, but we can modify the post query by passing a third parameter.
Modify the query for getEntityRecords
By passing an object as the third parameter to getEntityRecords we can modify the post query. As mentioned before unfortunately the documentation for getEntityRecords
is lacking. But by reading all over the web I’ve gathered a list of possible query arguments;
per_page
: Set to a number to limit the number of posts. Set to-1
to fetch the maximum of 100. Default10
.exclude
: Exclude certain posts from the query. Set to a post ID or an array of numbers for multiple post IDs.parent_exclude
: Exclude certain parent posts. Set to a post ID or an array of multiple post IDs.orderby
: Decide the order of posts. Most likely you can use the same parameters as in WP_Query’s orderby. Can be e.g. ‘menu_order
‘.order
: Either'asc'
or ‘desc'
for ascending or descending ordering.status
: Filter by post status. Can be a string, a string with multiple statuses separated by comma or an array of status strings. E.g.['publish', 'draft']
to query both published and drafted posts.categories
: Filter posts by certain categories. Provide a category ID or an array of category IDs. I believe this only works for post categories and not other custom taxonomies.tags
: Filter posts by certain tags. Provide a tag ID or an array of tag IDs. Works only for post tags and not other custom taxonomies.search
: Add a search query (string).
Let’s modify our query. We want to do two things; first we want to fetch all posts and not just the 10 latest. To do this we provide -1
to per_page
. Secondly we want to exclude the current post from the post list by providing the current post ID to exclude
. It often makes no sense to show a post shortcut or preview of the current post itself.
You might think; hold on, how do we get the current post ID? Don’t forget that we within the higher-order component withSelect
and with the select
selector can access all WordPress’ core data stores. The current post ID is a natural thing to store in one of WordPress core stores. Within core/editor
we find the function getCurrentPostId()
.
Let’s modify the withSelect
return to something like this:
edit: withSelect(select => { const currentPostId = select('core/editor').getCurrentPostId(); const query = { per_page: -1, exclude: currentPostId } return { posts: select('core').getEntityRecords('postType', 'post', query) } })(FirstBlockEdit),
The above change is pretty self-explanatory. We generate a query object with the properties per_page
and exclude
and pass this as third parameter to getEntityRecords()
. Now our props.posts
inside the FirstBlockEdit
component should list out all posts but exclude the current post.
Conclusion
This post concludes the How to create custom Gutenberg blocks tutorial series. The series was intended to go through the basics of developing your own custom blocks, providing you a starting point to develop your own and more complex blocks. Definitely keep an eye out for more Gutenberg related tutorials here. Maybe you’ll find a tutorial more specifically explaining something you wanted to do yourself!