WordPress Data Store Properties: Action Creator Generators

Now we have a resolver for asynchronously getting products via a REST api endpoint, we have some action creators, and we have a selector.  We haven’t got to the reducer yet, but before we get to it, let’s consider another problem we haven’t addressed yet. 

When we update or create a product using our ui, how are we going to persist that to the server? We could just wire up the POST/PUT endpoints directly in our component, but remember one of the reasons we’re implementing wp.data is to provide a common interface for any component needing to interact with the products data. So what can we do here?

Fortunately, we’ve covered a part of the wp.data api that provides us with a mechanism for doing this, controls. With controls, it is possible to make action creator generators that yield control action objects for handling asynchronous behaviour.  Essentially everything you’ve learned about resolver generators so far can also be applied to action creators!

So let’s go back to the action creators we’ve already prepared (reminder: src/data/products/actions.js) which are createProduct, updateProduct and deleteProduct. Right now these are not wired up to any API route. Let’s change that. If you feel adventurous, why don’t you give it a go in your own fork of the app right now and see what you come up with. You can come back and see how close you got after.

You back? Okay, here’s one approach you could end up with:

import { getResourcePath } from './utils';
import { fetch } from '../controls';
import { select } from '@wordpress/data-controls';
import TYPES from './action-types';
import STORE_KEY from './constants';

const { UPDATE, CREATE, DELETE, HYDRATE } = TYPES;

export function* createProduct(product) {
 const result = yield fetch(getResourcePath(), {
   method: "POST",
   body: product
 });
 if (result) {
   return {
     type: CREATE,
     product: result
   };
 }
 return;
}
 
export function* updateProduct(product) {
 const canonicalProduct = yield select(STORE_KEY, "getProduct", product.id);
 const result = yield fetch(getResourcePath(canonicalProduct._id), {
   method: "PUT",
   body: product
 });
 if (result) {
   return {
     type: UPDATE,
     product
   };
 }
}
 
export function* deleteProduct(productId) {
 const product = yield select(STORE_KEY, "getProduct", productId);
 const result = yield fetch(getResourcePath(product._id), {
   method: "DELETE"
 });
 if (result) {
   return {
     type: DELETE,
     productId
   };
 }
}

Take note of a few things here:

  • For updateProduct and deleteProduct I need to get the original product in the state because jsonbox.io references objects on the server via an _id property. I could have used the existing getProducts selector and then filtered the product from the state, but I decided to create a new selector to handle that (which also keeps things DRY).  Here’s what I ended up with as a new selector in selectors.js:
export const getProduct = (state, id) => {
 return getProducts(state).find(product => product.id === id);
};
  • To access this new selector, I used the select control from the @wordpress/data-controls package. Why? This ensures we’re accessing the selector from the same registry (the importance of this will be explained in more detail in a later post in the series).
  • I’m using the fetch control (remember this from the post about resolvers?)
  • I’m returning the original action object that was here before the updates.

Notice that the action creator is still returning an action object, the only change here is that with controls, we’re able to do some side-effects before returning that action object.

In the next post of this series, it’s time to go on to the last property of our store registration configuration object, the reducer.

Series NavigationWordPress Data Store Properties: ResolversWordPress Data Store Properties: Reducer

Leave a Reply

Up Next:

What is WordPress Data?

What is WordPress Data?