WordPress Data: Interfacing With the Store

This entry is part 12 of 15 in the series, A Practical Overview of the @wordpress/data API

Now that we have our new custom data store registered, we need to start integrating it with our example app. There are numerous ways in which you can interface with the data store in your components but I’m going to cover the four most common, straightforward ways.

withSelect( mapToProps )( Component)

withSelect is a higher order component that you use to connect your custom wp.data store with a component.

The argument you feed the curried function is a callback that maps values from selected state in wp.data to props that will then be fed through to the wrapped component. This callback you provide will receive two arguments: the first is select which is an interface to the wp.data registered stores, and ownProps which is the props that are passed into the component from parent components in the tree.

Here’s an example of withSelect being used:

import { withSelect } from '@wordpress/data';
import { STORE_KEY } from '../data';


const MyComponent = ( props ) => {
	return <div>{ props.content }</div>;
}

export default withSelect(
    ( select, ownProps ) => {
        return {
            content: select( STORE_KEY ).getContent( ownProps.type );
        };
    }
)( MyComponent );

Note, a few things about the implementation here:

  • select is called with a given store key (remember every data store has a namespace) and then it returns all the selectors registered to that data store.
  • you can use incoming ownProps as arguments to the invoked selector.
  • you return an object and this object will be merged with the props fed to the wrapped component.

Some other things to remember about the mapping callback you provide withSelect:

  • This callback is automatically subscribed to state changes in any registered store in the store registry. So it’s completely normal to see this invoked numerous times in the lifecycle of a component.
  • You can return undefined from your callback if there’s some condition where there are no props to inject.

It’s little known that the mapToProps function provided to withSelect will also receive a third argument which is the registry. We’ll get more into what this registry object is in the next post of the series, but for now understand that this exposes the dispatch function which allows you to also dispatch actions within your callback.

useSelect( mapToProps, dependencies )

useSelect is the newer (and preferred) way of interacting with your data stores (and under the hood, withSelect is now actually powered by useSelect). It is similar in functionality to withSelect in that you provide a mapping callback and return values, but it’s not a higher order component, rather, it is a custom React Hook.

The callback you provide to useSelect will receive select as the first argument and registry as the second argument, so it is very similar to withSelect, however any props you use will be bound from the scope of the initial function creation. This means, that if you are using props within the callback, you need to make sure you declare them as dependencies via the second argument passed to the hook so that React knows when to recreate memoized things using those values so any invocations are not using stale values. You can read more about how dependencies work here.

Here’s the same example we used for withSelect converted to use useSelect:

import { useSelect } from '@wordpress/data';
import { STORE_KEY } from '../data';

const MyComponent = ( props ) => {
    const content = useSelect( ( select ) => {
        return select( STORE_KEY ).getContent( props.type );
    }, [ props.type ] );
    return <div>{ content }</div>;
}

One major difference between the mapping callback you provide to useSelect and withSelect is that for useSelect you can return whatever you want whereas from withSelect it must be an object or undefined. It’s important to remember however that you must handle conditions where the selector might not have fully resolved yet (if it has a related resolver). So be aware of what stores you are selecting from and what the selectors might return.

withDispatch( mapPropsToDispatch )( Component )

Like withSelect, withDispatch is a higher order component that wraps whatever component you want to enhance with functions for dispatching actions to registered wp.data stores. It too receives a mapPropsToDispatch function with similar argument shape as withSelect except the first argument is the registry dispatch function which you use to dispatch actions from a specific store (or stores). The second argument is ownProps and the third argument is the registry instance which you can use to grab the select function for retrieving some dynamic data from the store before the action is fired.

While withSelect and withDispatch are similar in some ways, it’s important to remember that withDispatch requires the mapping callback to return an object that has functions as property values. It will throw console warnings if you don’t follow this rule.

Here’s an example of withDispatch in use (building off of our withSelect example):

import { withDispatch, withSelect } from '@wordpress/data';
import { compose } from '@wordpress/compose';
import { STORE_KEY } from '../data';


const MyComponent = ( props ) => {
	return <div onClick={ props.clickedContent } >{props.content}</div>;
}

export default compose(
    withSelect(
        ( select, ownProps ) => {
            return {
                content: select( STORE_KEY ).getContent( ownProps.type );
            }
        }
    ),
    withDispatch(
        ( dispatch, ownProps ) => {
            return {
                clickedContent: ownProps.type === 'general' ?
                    dispatch( STORE_KEY ).clickedGeneralContent :
                    dispatch( STORE_KEY ).clickedContent
            };
        }
    )
 )( MyComponent );

Note, in this example, we’ve also utilized a special function from the @wordpress/compose package. This function makes it easier to compose higher order components together to make the code easier to read. Without it, we would have had to write something like this:

export default withSelect( 
  ( select, ownProps ) => {
    return {
        content: select( STORE_KEY ).getContent( ownProps.type );
    }
  } )(
    withDispatch(
      ( dispatch, ownProps ) => {
          return {
              clickedContent: ownProps.type === 'general' ?
                  dispatch( STORE_KEY ).clickedGeneralContent :
                  dispatch( STORE_KEY ).clickedContent
          };
      }
    )( MyComponent )
  );

Which example do you find easier to read?

useDispatch( storeKey )

useDispatch is probably the most straightforward out of all the interfaces we’ve looked at so far because it only receives one argument and that is simply the store you wish to retrieve actions from. Calling useDispatch with a store key will return all the action creators registered to that store namespace as properties in an object.

Using the same example I’ve been demonstrating with in this post, here’s how we’d do things with useSelect and useDispatch.

import { useDispatch, useSelect } from '@wordpress/data';
import { STORE_KEY } from '../data';

const MyComponent = ( props ) => {
    const content = useSelect( ( select ) => {
        return select( STORE_KEY ).getContent( props.type );
    }, [ props.type ] );
    const { clickedContent, clickedGeneralContent } = useDispatch( STORE_KEY );
    const onClick = props.type === 'general' ?
        clickedGeneralContent :
        clickedContent;
    
    return <div onClick={ onClick }>{ content }</div>;
}

Notice that this is a much shorter block of code than the example used for withDispatch. This is one of the reasons why useSelect and useDispatch are generally preferable to use over withSelect and withDispatch but there are still some cases where you might want to use the latter. For example, if you’re working with older code that is still using class components, it might be easier to implement withSelect and withDispatch initially then to refactor the component to a functional component.

One thing you can remember with useDispatch is that the returned action functions will never change (unless the registry in the tree the component belongs to changes). So generally, while you shouldn’t need to include these as dependencies in hooks using them (similar to the setState function from useState), it’s still a good idea to.

Of course, these four interfaces are just the most common way to select data from registered wp.data stores and dispatch actions. There are others, but I’m not going to cover them here. You may also want to read another post I wrote, “Fantastic hooks and where to use them” in which I share a bit more on the benefits of using the hooks over the hocs (and some insight into the progression of developer quality of life between the low level wp.data interfaces all the way to the hooks when in a react context).

Finally, you now have enough knowledge through the series so far to convert the app you’ve been working on over to use wp.data instead of tree state for product data. It’d be good for you to try and implement the interfaces above in your fork of the app now. But if you are stuck, here’s the completed conversion of the app.

Series NavigationWordPress Data: Putting it all together and registering your store!WordPress Data: Registry

Leave a Reply

Up Next:

WordPress Data Store Properties: Controls

WordPress Data Store Properties: Controls