Post information in Gutenberg’s edit function
Ever needed to retrieve and render Post information inside your block’s edit function (e.g. show News filtered by a tag attribute)? In this article I explain two different approaches to go about this; using Gutenberg’s state & using WordPress’ JSON API.
Retrieving post information from Gutenberg’s state
Gutenberg comes with its own Redux stores (state containers) that store the editor’s state and share it among blocks. This state contains information about posts, users, taxonomies etc.
In order to access the editor’s state inside your block you need to bind your block with the Redux state. To achieve that, you need to use WordPress’ helper functions that come as part of the package @wordpress/data.
Installing @wordpress/data
So, firstly install the npm package in your plugin using npm:
npm i @wordpress/data --save-dev
Or using yarn:
yarn add @wordpress/data --dev
Once you’ve installed this package you’ll be able to load the helper functions to use in your block’s edit function. Since this article explores how to retrieve post information, for this case we’re going to use the helper function withSelect
.
Using withSelect
The withSelect
function takes one argument – a function that will run on every state change and applies the result to a WordPress Component (WPComponent), e.g. an edit function component. As a result, it returns a WPComponent that contains the original component plus extra properties retrieved from the state. In practice this function is used like this:
import { withSelect } from '@wordpress/data';
// Custom News block edit function
const NewsEdit = ( { prop1, prop2 } ) => {
// your edit functionality with access to this.props.news
}
// Injecting state properties to our custom News block edit function
const NewsEditWithStateProps = withSelect( ( select, props ) => {
// we select the state props here and return them as properties
// e.g.
// return {
// news: [...]
// };
} )( NewsEdit );
// Registering our custom News block
registerBlockType(
'custom-news-block',
{
// [...] other block settings
edit: NewsEditWithStateProps
}
);
In the example above, NewsEditWithStateProps
enables NewsEdit
to access this.props.news
which contains state-driven information and which will update every time its state is updated.
Real world example of withSelect
Here is an example where I return News (Posts) filtered by a tag (tag being one of the block’s attributes):
/**
* External dependencies
*/
import { isUndefined, pickBy } from 'lodash';
/**
* WordPress dependencies
*/
import { withSelect } from '@wordpress/data';
// Custom News block edit function
const NewsEdit = ( { prop1, prop2 } ) => {
// your edit functionality with access to this.props.news
}
// Injecting posts filtered by tag to the NewsEdit WPComponet
const NewsEditWithStateProps = withSelect( ( select, props ) => {
const { tag } = props.attributes;
const { getEntityRecords } = select( 'core' );
const customTagQuery = pickBy( {
tags: tag
}, (value) => !isUndefined(value) );
return {
news: getEntityRecords( 'postType', 'post', customTagQuery ),
};
} )( NewsEdit );
// Registering our custom News block
registerBlockType(
'custom-news-block',
{
// [...] other block settings
edit: NewsEditWithStateProps
}
);
In this example:
- I extract the
getEntityRecords
function on line 18 from the WordPress “core” Redux store which will allow me to perform a custom query, - I then construct the custom tags query on lines 19-21 given that the
tag
attribute is notundefined
, - And finally, I call
getEntityRecords
for the post type “post” and the custom tags query and Ι return the result as the propertynews
on lines 22-24.
This approach is most appropriate when you need the post information inside your block to update based on changes that happen inside the Gutenberg editor. Alternatively, when your block needs post information once that will not update in relation to the editor’s state, you can query the WordPress JSON API as explained in the following section.
Retrieving post information from WordPress’ JSON API
As explained in the previous section, there are cases where you need to retrieve post (or taxonomy) information but don’t need it to update based on the Gutenberg editor’s state (e.g. a list of all the categories). For this case, a good alternative to using Redux stores is to query WordPress’ JSON API.
In order to query WordPress’ JSON API inside your components it’s best to use WordPress’ helper functions that come as part of the package @wordpress/api-fetch.
Installing @wordpress/api-fetch
So, firstly install the npm package in your plugin using npm:
npm i @wordpress/api-fetch --save-dev
Or using yarn:
yarn add @wordpress/api-fetch --dev
Once you’ve installed this package, you’ll have access to the helper function apiFetch – a wrapper function of window.fetch. With apiFetch you’ll be able to make custom HTTP requests inside your component.
Using apiFetch
The apiFetch function takes an options object as an argument and returns a Promise. That Promise
will resolve with a request Response or with a network error.
Since apiFetch is a wrapper function of window.fetch
it supports all the options of that function plus the following options:
The best place to call apiFetch is in the lifecycle hook componentDidMount. This will ensure the function is called once when the component is mounted.
Additionally, you can hook into componentWillUnmount to stop any outstanding requests from updating the component’s state after the component is unmounted.
In practice an apiFetch call in an edit WPComponent will look like this:
/**
* WordPress dependencies
*/
import { Component } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';
export default class BlockEdit extends Component {
constructor() {
super( ...arguments );
// Initialising the component's state
this.state = {
isStillMounted: false,
response: {},
};
}
// Component did mount lifecycle hook
componentDidMount() {
// Setting this.isStillMounted to true
this.setState( {
isStillMounted: true
} );
// Performing an HTTP request to the WP JSON API
apiFetch( {
path: 'WP-JSON-API-PATH-GOES-HERE',
} )
// on success
.then(
( response ) => {
// if the component is still mounted, update the state with the new response
if ( this.isStillMounted ) {
this.setState( { response } );
}
}
)
// on error
.catch(
() => {
// if the component is still mounted, set the response to an empty object
if ( this.isStillMounted ) {
this.setState( { response: {} } );
}
}
);
}
// Component will unmount lifecycle hook
componentWillUnmount() {
// Set this.isStillMounted to false so that any outstanding HTTP requests
// don't attempt to update this.response
this.setState( {
isStillMounted: false
} );
}
render() {
// I can now use response inside the render function
const { response } = this.state;
}
}
In this example, I call apiFetch inside componentDidMount() and I only update the component’s state with the response if the component is still mounted.
Real world example of apiFetch
Following is an example of an edit WPComponent where I retrieve the first 10 (default per_page) categories and render them in an unordered list.
/**
* WordPress dependencies
*/
import { Component } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';
export default class CategoriesEdit extends Component {
constructor() {
super( ...arguments );
// Initialising the component's state
this.state = {
isStillMounted: false,
categories: [],
};
}
// Component did mount lifecycle hook
componentDidMount() {
// Setting this.isStillMounted to true
this.setState( {
isStillMounted: true
} );
// Performing an HTTP request to the WP JSON API
apiFetch( {
path: '/wp/v2/categories',
} )
// on success
.then(
( categories ) => {
// if the component is still mounted, update the state with the new categories
if ( this.isStillMounted ) {
this.setState( { categories } );
}
}
)
// on error
.catch(
() => {
// if the component is still mounted, set the categories to an empty array
if ( this.isStillMounted ) {
this.setState( { categories: [] } );
}
}
);
}
// Component will unmount lifecycle hook
componentWillUnmount() {
// Set this.isStillMounted to false so that any outstanding HTTP requests
// don't attempt to update this.categories
this.setState( {
isStillMounted: false
} );
}
render() {
const { categories } = this.state;
return (
<ul>
{ categories.map( ( category ) => {
return (
<li
key={ category.slug }>
{ category.name }
</li>
);
} ) }
</ul>
)
}
};
If you wish to add arguments to your apiFetch call, you can use the helper function addQueryArgs that comes with the package @wordpress/url.
For example, to return all the categories in the example above we would change the lines 26-28 with:
apiFetch( {
path: addQueryArgs( '/wp/v2/categories', { per_page: -1 } ),
} )
Setting per_page to -1 will return all the categories. In order to use addQueryArgs
you will need to install the package @wordpress/url
to your plugin, which you can do with npm:
npm i @wordpress/url --save-dev
Or with yarn:
yarn add @wordpress/url --dev
You will also need to import addQueryArgs
alongside with your other WordPress dependencies like so:
import { addQueryArgs } from '@wordpress/url';
Conclusion
In this article I explained two completely different approaches for rendering post information inside a custom block’s edit function.
The first approach (using Gutenberg’s state) is appropriate when you need to update your component’s attributes when an editor store is updated. For example, when you wish to filter News by a tag based on a tag attribute change.
The second approach (using WordPress’ JSON API) is appropriate when you need to render post information which doesn’t rely on editor state changes. For example, when you want to list all the available Post categories.
Choosing the appropriate approach depends on whether you wish to update your component with every editor update (first approach) or whether you wish to retrieve information once after mounting your component (second approach).