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 not undefined,
  • And finally, I call getEntityRecords for the post type “post” and the custom tags query and Ι return the result as the property news 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).