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

To recap: so far in the series we’ve looked at the various properties of the configuration object for a store:

  • selectors: which interface with the store for retrieving data from its state.
  • actions: (actually action creators) which are used to dispatch action objects for handling by the reducer.
  • reducer: which receives dispatched actions and determines what happens to the state as a result.
  • controls: allowing for asynchronous side-effects on action generators or resolvers.
  • resolvers: linked to selectors, resolvers allow for automatically resolving data for the initial slice of state the selector is retrieving for.

This post will be short and sweet as we simply look how we can take all this and register the custom store!

At the beginning of the series is a post that gave an example for registering a custom store. In that post is an example for how to register the store. Why don’t you take a few minutes and go back and view that example and using it as a reference, update the example app you’re converting to use a custom store so that you register the store with all the properties you’ve created in this series.

Done? Great! Let’s compare. Here’s what I came up with – in src/data/products/index.js I have the following:

import * as selectors from "./selectors";
import * as actions from "./actions";
import reducer from "./reducer";
import * as resolvers from "./resolvers";
import { controls as wpControls } from "@wordpress/data-controls";
import localControls from "../controls";

export { default as STORE_KEY } from "./constants";
export const STORE_CONFIG = {
  selectors,
  actions,
  reducer,
  resolvers,
  controls: { ...wpControls, ...localControls }
};

And then in src/data/index.js I have the following:

import {
  STORE_KEY as PRODUCTS_STORE_KEY,
  STORE_CONFIG as productsConfig
} from "./products";
import { registerStore } from "@wordpress/data";

registerStore(PRODUCTS_STORE_KEY, productsConfig);

export { PRODUCTS_STORE_KEY };

You might have something similar, but instead have it all in one file. The only reason why I’ve broken this up into two files is because eventually I’m probably going to add a store for the cart. So this allows for keeping the configuration for the stores separate and then pulling them in for the actual registration.

I do want to draw attention to how I’m exporting the store key (eventually as PRODUCTS_STORE_KEY) from the main entrypoint. Why would you want to do this? There are a couple reasons:

The first reason is to guard against human error with typos etc when interfacing with the store in code that consumes it. We haven’t got to this yet, but anywhere you use a wp.data interface for retrieving data from a store, you need to use the store key to interact with the correct store. Here’s a quick example to illustrate:

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

export const MyComponent = () => {
    const item = useSelect( ( select ) => select( 'my/store' ).getItem() );
    return <div>{ item }</div>;
}

Remembering what the string you used for a store key can be a pain and there is the risk of making a typo with my/store. Whereas if you are using constants:

import { MY_STORE_KEY } from '../data/my-data';
import { useSelect } from '@wordpress/data';

export const MyComponent = () => {
    const item = useSelect( ( select ) => select( MY_STORE_KEY ).getItem() );
    return <div>{ item }</div>;
}

Not only do you not have to remember the string but most IDE’s will also auto-complete for you so it makes it even easier.

The second reason for exporting your store key on a named constant is for when you are using wp.data in a WordPress environment and thus enqueuing your scripts php side.

If you are using the @wordpress/scripts package for building your javascript, it will be using the @wordpress/dependency-extraction-webpack-plugin to automatically detect wp global dependencies wherever you are importing from @wordpress/* packages. You can also configure this webpack plugin to export other custom dependencies (such as aliased packages as externals) and you may have your registered data store exported to it’s own bundle.

For example, in some projects I have done, I alias my data store registration to it’s own bundle so I only enqueue that specific store when it’s needed (let’s say @myplugin/data ), then I configure the dependency-extraction plugin so that it correctly detects @myplugin/data is a dependency built to a script that will be loaded on the script handle my-plugin-data. That’s all fine and dandy, but if I’m using my custom store like this:

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

export const MyComponent = () => {
    const item = useSelect( ( select ) => select( 'mystore/data' ).getItem() );
    return <div>{ item }</div>;
}

…I’ll get an error with getItem() not being defined because mystore/datahas not been registered (unless I explicitly remember to enqueue my script with my store registration bundle as a dependency instead of relying on the dependencies extracted by the webpack plugin).

However, if I do this:

import { MY_STORE_KEY } from '@myplugin/data';
import { useSelect } from '@wordpress/data';

export const MyComponent = () => {
    const item = useSelect( ( select ) => select( MY_STORE_KEY ).getItem() );
    return <div>{ item }</div>;
}

Then the webpack dependency extraction plugin will correctly pick up that this script has a dependency on @myplugin/data and thus provide it’s associated script handle for the generated dependency array.

Now that you have code in place to register your custom store, it’s time to start looking at how to interface with that store in your components. We’ll get to that in the next post.

Series NavigationWordPress Data Store Properties: ReducerWordPress Data: Interfacing With the Store

Leave a Reply

Up Next:

WordPress Data Store Properties: Controls

WordPress Data Store Properties: Controls