Using useSelect in Gutenberg to fetch data from the REST API

The WordPress Gutenberg method wp.data.withSelect allows us to wrap a component in a React Higher Order Component, which adds functionality or adds data to the props of the wrapped component. But sometimes, all we need is a simpler method of just fetching some data — e.g. posts — from the REST API and then rendering the component using these posts.

withSelect and HOC is very powerful, but there’s an easier way: the useSelect method. We can insert this method call directly in our component and render the component depending on whether the data is available or not. This makes the code much leaner and much more maintainable for developers with less experience. This solution is also a great deal easier to copy and paste for reuse across many components.

I’ve used the technique in the edit component of a WordPress Gutenberg Block today, in which I need to create a SelectControl to allow the user to select a page. (I’ve since modified the interface to output a TreeSelect component instead of a SelectControl, but that adds more complexity that is unnecessary for this blog post.)

import { InspectorControls } from "@wordpress/block-editor";
import { Fragment, PanelBody, SelectControl, Spinner } from "@wordpress/components";
import { useSelect } from "@wordpress/data";
import { _x } from "@wordpress/i18n";

const Edit = props => {
    const { attributes, setAttributes } = props;

    const { post_id } = attributes;

    // The value of 'posts' will be false until the REST API responds asynchronously
    const { posts } = useSelect(select => {
        return {
            posts: select("core").getEntityRecords("postType", "page", {
                per_page: 100,
                order: "asc",
                order_by: "menu_order",
            }),
        };
    });

    const options = [];

    // Make an array from the REST API response if posts are available
    if (!!posts) {
        Object.values(posts).forEach(post => {
            options.push({
                value: post.id,
                label: post.title.rendered ? post.title.rendered : _x('No title', 'SelectControl option label', 'sha'),
            });
        });
    }

    return (
        <Fragment>
            <InspectorControls>
                <PanelBody title={_x("Settings", "PanelBody title", "sha")}>
                    {!posts && <Spinner />}
                    {!!posts && (
                        <SelectControl
                            label={_x(
                                "Select a page",
                                "SelectControl label",
                                "sha"
                            )}
                            value={post_id}
                            options={options}
                            onChange={post_id => setAttributes({ post_id })}
                        />
                    )}
                </PanelBody>
            </InspectorControls>
        </Fragment>
    );
};

export default Edit;

The code in this post was updated on 27th January, in order to fix an error when building the options array.

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.