WordPress.org

WordPress Developer Blog

useEntityRecords: an easier way to fetch WordPress data

There are many reasons why you would want to fetch data from WordPress’ REST API and use it in your application. 

One common example is when creating a dynamic block. A dynamic block doesn’t have a save() function and instead uses server-side rendering to render the content in the front end. If you want your block to look the same way in the editor as it does in the front-end, then your block’s edit() function will need to fetch the content from the REST API.

That’s just one example. You can probably think of many more.

The @wordpress/data and @wordpress/core-data packages provide a number of actions and selectors that make it easier to fetch data, and perform other CRUD operations (CRUD stands for Create, Read, Update, Delete). Some examples of these actions and selectors include getEntityRecords, editEntityRecord, and deleteEntityRecord, among others.

There’s a very good course, Using the WordPress Data Layer, over on Learn WordPress, that walks you through the process of performing CRUD operations using these packages. That course demonstrates another example where you might want to fetch data, i.e. to display it on an admin page.

Following the example used in that course, this tutorial will show you how to fetch a list of pages. But rather than displaying the list on an admin page, you will instead display it in a block.

Before you start, you’re going to need to have some pages to display. The quickest and easiest way to do this is to import the Theme Unit Test data. The screenshots in this tutorial will show the block displaying pages created by Theme Unit Test.

Creating the plugin

Now that you have some pages in your database, you’re ready to start building a block that will fetch them and display the page titles in a list.

The @wordpress/create-block package is a great way to quickly scaffold a plugin that will create such a block. For these purposes you will create a dynamic block called list-pages. Run the following command from within the wp-content/plugins directory of your local test site:

npx @wordpress/create-block --variant dynamic list-pages

The scaffolding will take a minute or two. Once it’s done, activate the plugin. Then create a new post, and add a “List Pages” block to the post.

Save the post.

You will see this in the editor:

And this in the front-end:

Let’s start by changing the front-end view to show a list of pages as this is fairly straightforward.  It’s just a WP_Query that fetches the 20 most recent pages, and this is followed by a pretty standard implementation of the posts loop that displays the page titles in an unordered list, i.e. as <li>‘s in a <ul>, with each one linking to the relevant page. If there are no pages a message indicating that this is the case is displayed instead.

Add this code to the render.php file in the src directory. (You will be working exclusively in the src directory in this tutorial, so for all future references to files you should assume that they are in the src directory)

<?php
$args = array(
  'post_type' => 'page',
  'posts_per_page' => '20'
);

$pages = new WP_Query( $args );

if ( $pages->have_posts() ) {

  $page_list = '<ul>';

  while ( $pages->have_posts() ) {
    $pages->the_post();
    $page_list .= '<li><a href="'. get_the_permalink() . '">' . get_the_title() . '</a></li>';
  }
  
  $page_list .= '</ul>';

} else {
  $page_list = 'No pages to display.';
}

wp_reset_postdata();
?>

<div <?php echo get_block_wrapper_attributes(); ?>>
  <?php echo $page_list; ?>
</div>

Save the file and run npm start from within the wp-content/plugins/list-pages directory. This will ensure that the changes are propagated to the build directory and will also watch for future changes and compile them.

Now if you refresh the front-end you will see this, assuming you’ve followed the suggestion to import the Theme Unit Test data:

By the way, if you want your version to look the same, style li a in style.scss:

.wp-block-create-block-list-pages {

  background-color: #d5e7ee;
  padding: 2px;

  li a {
    color: rgb(32, 81, 167);
  }

}

Viewing the list in the editor

At the moment the editor still shows the same as it did before:

Ideally the editor should look something like the front-end. Let’s see how we can fetch the same content into the block in the editor.

Let’s start by using getEntityRecords, as they did in the course mentioned earlier. Then you’ll see later on how useEntityRecords uses less code and makes it a whole lot easier to do things like checking whether the query has resolved yet.

First, create a <PagesList> component in edit.js:

function PagesList( { pages } ) {

  if ( !pages?.length ) 
    return <div>No pages to display</div>;

  return (
    <ul>
      { pages?.map( page => (
        <li key={ page.id }>
          { page.title.rendered }
        </li>
      ) ) }
    </ul>
  );

}

This component destructures pages from the props object passed to it, and returns an unordered list consisting of the titles taken from the array of pages. If the array is empty, i.e. there are no pages, it will instead return a string, “No pages to display”.

This component does, of course, need an array of pages passed to it, so let’s fetch that from the WordPress database.

Add these two import statements to the top of edit.js:

import { useSelect } from '@wordpress/data';
import { store as coreDataStore } from '@wordpress/core-data';

Next, replace the Edit() function with this one:

export default function Edit() {

  const pages = useSelect(
    select =>
      select( coreDataStore ).getEntityRecords( 'postType', 'page', { per_page: 20 } ),
    []
  ); 

  return <PagesList pages={ pages }/>;

}

useSelect is a custom React hook imported from the @wordpress/data package. It receives a callback function and an array of dependencies (which in this case is empty).

The callback function uses the store’s getEntityRecords function to retrieve the twenty most recent pages – this is defined in the query object that is the third parameter passed to getEntityRecords. The other two parameters are the entity kind, here postType, and the entity name, in this case page.

Now if you reload the editor page your block should look something like this:

But there’s a problem!

If you watch carefully you’ll notice that the “No pages to display” text appears for a short while as the data is still loading. It would be better to replace this with a loading indicator so that “No pages to display” only displays if there are indeed no pages.

Adding a loading indicator

To do this, start by importing the <Spinner> component:

import { Spinner } from '@wordpress/components';

Next, the useSelect callback function needs to return an object. pages is no longer an accurate name for the const, so change it to instead be called data:

const data = useSelect(

    select => {
      return {
        pages: select( coreDataStore ).getEntityRecords( 'postType', 'page', { per_page: 20 } ),
        hasResolved: select( coreDataStore ).hasFinishedResolution( 'getEntityRecords', ['postType', 'page', { per_page: 20 } ] )
      }
    },
    []

);

The results from the previous getEntityRecords query is now being stored in the object’s pages property, and the object also has an additional property, hasResolved, which contains a boolean returned by hasFinishedResolution that is false while the query is still resolving.

Pass both of these as props to the <PagesList> component:

<PagesList hasResolved={ data.hasResolved } pages={ data.pages } />

Then, over in the <PagesList> component destructure the additional prop, check for its value, and display the <Spinner> component if it’s false:

function PagesList( { hasResolved, pages } ) {

  if ( !hasResolved ) 
    return <Spinner />;
  ...

}

Your entire edit.js file should now look like this:

import { useBlockProps } from '@wordpress/block-editor';
import { useSelect } from '@wordpress/data';
import { store as coreDataStore } from '@wordpress/core-data';
import { Spinner } from '@wordpress/components';

export default function Edit() {

  const data = useSelect(
    select => {
      return {
        pages: select( coreDataStore ).getEntityRecords( 'postType', 'page', { per_page: 20 } ),
        hasResolved: select( coreDataStore ).hasFinishedResolution( 'getEntityRecords', ['postType', 'page', { per_page: 20 } ] )
      }
    },
    []
  ); 

  return (
    <div { ...useBlockProps() }>
      <PagesList hasResolved={ data.hasResolved } pages={ data.pages } />
    </div>
  );

}

function PagesList( { hasResolved, pages } ) {

  if ( !hasResolved ) 
    return <Spinner />;
  
  if ( !pages?.length ) 
    return <div>No pages to display</div>;

  return (
    <ul>
      { pages?.map( page => (
        <li key={ page.id }>
          { page.title.rendered }
        </li>
      ) ) }
    </ul>
  );

}

This code is fairly convoluted, requiring separate function calls to fetch the data and to determine if the data is still being fetched – both of which need to be passed exactly the same query parameters. Furthermore, it requires close reading to understand it properly. 

Using useEntityRecords instead

Let’s now see how we can achieve the same thing in a cleaner, more elegant, and easier-to-use method by using useEntityRecords instead.

useEntityRecords is a custom React Hook, as is its singular cousin useEntityRecord. They’re effectively wrappers for getEntityRecords and getEntityRecord respectively. This can clearly be seen in the source code for useEntityRecords.

A React Hook is a JavaScript function used inside a functional component that “hooks into” React state and lifecycle methods. As such Hooks can be stateful or can manage side-effects. React provides several built-in Hooks such as useState and useEffect, but it’s also possible to create custom Hooks in your application, which is precisely what the Gutenberg developers have done, and useEntityRecords is an example of such a custom Hook.

As an aside, custom React Hooks are not to be confused with WordPress hooks or the @wordpress/hooks package. They are something else entirely.

React Hooks must be called at the top level of the component, i.e. they can’t be used within conditionals or loops. There are also other rules for using hooks that should be borne in mind.

So now let’s dig into the useEntityRecords hook to see how using it can reduce the amount of code we need to write in order to perform a fetch similar to the one above that used getEntityRecords. You’ll continue to work with the existing edit.js file.

First up, import useEntityRecords from the core data package:

import { useEntityRecords } from '@wordpress/core-data';

You don’t need to import the store anymore as it is already being imported into use-entity-records.ts which is the file where the useEntityRecords hook is defined. And you’re no longer going to be using useSelect, so you can remove these two import statements:

import { useSelect } from '@wordpress/data';
import { store as coreDataStore } from '@wordpress/core-data';

And now for the magic 🪄. The somewhat impenetrable code of the useSelect statement used previously can now be replaced with a single line that instead uses the useEntityRecords hook:

const data = useEntityRecords( 'postType', 'page', { per_page: 20 } );

useEntityRecords can take four parameters, the first two of which are required. The first is kind which can be either root or postType, and the second is the name of the entity you want to retrieve – in this example it’s page but it could also be any other post type, including the name of a custom post type (CPT).

The third parameter, which is optional, is an object containing the query arguments. The useEntityRecords hook can take exactly the same query arguments that getEntityRecords does. For example, if you wanted to fetch 20 records instead of the default 10 that the WordPress REST API returns you could add an object with a per_page property and pass it as the third parameter to useEntityRecords, as has been done here.

To find the query arguments that your chosen entity type accepts consult the REST API reference and select your chosen entity type. So in the case of pages, which is the entity type used in this tutorial, select ‘Pages’ from the list and locate the Arguments that can be passed. You’ll notice that they’re a subset of the parameters that can be passed to an instance of the WP_Query class in PHP.

useEntityRecords returns an object with four properties:

  • hasResolved (boolean)
  • isResolving (boolean)
  • records (array)
  • status (string – one of: Resolving, Success, Error, Idle)

Using these properties you can determine if the fetch request is still resolving, or whether it has resolved. Once it has resolved, the data (if any) will be in the records property. There’s also a status property which can be checked in case there’s an error fetching the data.

You can see all this if you temporarily add a console.table( data ) line just after the data const has been defined:

In this screenshot showing the output of console.table( data ) you can see that in this case the fetch is still resolving, hence isResolving is true and hasResolved is still false. As the operation is still resolving there is not yet any data in the records property, hence its value is null. Once the operation has finished resolving the records property will contain an array with the fetched data in it, and  isResolving and hasResolved will be false and true respectively.

For the loading indicator you can either check for the truthiness of isResolving or of !hasResolved. Following the earlier example we’ll use !hasResolved.

There’s just one minor change that you need to make before you can test it in the editor. Because useEntityRecords defines records as the property containing the pages fetched you need to reference that as the props passed to the <PagesList> component:

<PagesList hasResolved={ data.hasResolved } pages={ data.records } />

The <PagesList> component can remain as it is. Your edit.js file should now look like this:

import { useBlockProps } from '@wordpress/block-editor';
import { useEntityRecords } from '@wordpress/core-data';
import { Spinner } from '@wordpress/components';
import './editor.scss';

export default function Edit() {

  const data = useEntityRecords( 'postType', 'page', { per_page: 20} );
	
  return (
    <div { ...useBlockProps() }>
      <PagesList hasResolved={ data.hasResolved } pages={ data.records } />
    </div>
   );

}

function PagesList( { hasResolved, pages } ) {

  if ( !hasResolved ) 
    return <Spinner />;
 
  if ( !pages?.length ) 
    return <div>No pages to display</div>;

  return (
    <ul>
      { pages?.map( page => (
         <li key={ page.id }>
           { page.title.rendered }
         </li>
      ) ) }
    </ul>
  );

}

Save the file and reload the page in the editor. This version is much easier to understand now, and the functionality in the editor should remain unchanged – i.e. the block should still display the same twenty page titles.

Finally…

You may well have realized that you could also destructure the object returned by useEntityRecords to make the props syntax terser:

export default function Edit() {

  const { hasResolved, records } = useEntityRecords( 'postType', 'page', { per_page: 20} );
	
  return (
    <div { ...useBlockProps() }>
      <PagesList hasResolved={ hasResolved } pages={ records } />
    </div>
  );

}

Either way is fine – it’s your choice.

If you want to take things further and explore some of the other custom React Hooks built into the @wordpress/core-data package the documentation can be found here.

Props to @greenshady, @welcher, @ndiego@marybaum, and @bph for reviewing this post and providing useful and constructive feedback.

2 responses to “useEntityRecords: an easier way to fetch WordPress data”

  1. Sajid Avatar

    I want to filter custom posts by a taxonomy. How can I do it with useEntityRecords()? I didn’t find documentation where parameters are specified for it.

  2. gresch Avatar

    Hi Michael,

    Thanks very much for this very informative article which helped me a lot!

    There’s one detail I can’t get running: changing getEntityRecords/useEntityRecords so I can add metadata to the query. I tried with an approach like so:

    “`js
    const query = {
    relation: ‘AND’,
    search: searchTerm ? searchTerm : null,
    metaQuery: [
    {
    key: ‘theMetaKey’,
    value: postId, /* dynamic */
    compare: ‘=’,
    type: ‘NUMERIC’,
    },
    ],
    };
    “`
    But this doesn’t seem to work. If https://developer.wordpress.org/rest-api/reference/posts/#schema-meta is supposed to be supported, this approach should work, correct?

Leave a Reply

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