This blog post is sponsored by my WordPress and design agency Say Hello.
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;
Addendum 6th February: if you’re only getting a single dataset, you can simplify the usage of useSelect
in the example above by directly assigning the result of the API call to the posts
constant.
const posts = useSelect(select => {
return select("core").getEntityRecords("postType", "page", {
per_page: 100,
order: "asc",
order_by: "menu_order",
}),
});
Addendum 29th July: if you’re also looking to update data on the server using this non-TOC method, then you can use the editPost
method from useDispatch
in a similar way. (In the example, the handleMetaValueChange
callback would be used when e.g. a SelectControl
change event is fired.)
const { editPost } = useDispatch('core/editor');
const handleMetaValueChange = (main_offset) => {
editPost({ meta: { main_offset } });
};