Using the WordPress JavaScript APIs for fun and profit part three - Connecting to the WordPress API

Welcome to part three of our series on using the WordPress JavaScript API’s, in which we explore the API’s that were introduced in WordPress 5.0. And, we'll look at how we can use them to better integrate with other plugins, in a reliable and safe way. Make sure to also check out our repository on GitHub, which contains all the code we'll be writing in this series.

Up until now, we’ve been adding code to the editor that has no relation to WordPress’ APIs. In the first part of this series, we've explored the basics and in the second part, we started connecting the data that we fill in this form, to a central data store by using Redux.

In this chapter, we’ll be migrating over from our own bare bone Redux implementation to one that uses the WordPress-style of data management.

The difference between Redux and @wordpress/data

Although WordPress has built their API on top of Redux, we’ll still need to make some adjustments to our code to make sure we’re compatible. A few interesting differences between Redux and the WordPress API are as follows:

Stores have namespaces

WordPress’ implementation uses namespaces to allow plugins to isolate their data in smaller ‘sections’ within the main store. This ensures that plugin data doesn’t conflict with other stores that might use the same names for certain value. This has the added bonus of making it very clear to other plugin developers where certain data lives within the application. Using this setup results in a uniform way of storing and retrieving data, which makes plugin development easier.

Stores are registered globally

A pattern that WordPress is (in)famous for, is the usage of using globals. This is no different within their JavaScript API, which uses the same principle to make registering stores easier for developers.

Data is retrieved from the store via selectors

WordPress’ API introduces the concept of selectors. Selectors are a set of functions, used to retrieve and derive data from the store, so you don’t have to alter or read in raw data.

React Redux is no longer necessary

WordPress has its own implementation of React Redux’ connect method that basically achieves the same goal, but in a slightly different way. Therefore, we no longer need to use React Redux as a dependency.

Implementing @wordpress/data into our code

Prerequisites

Now that we’ve discussed what the package entails, lets start implementing it into our code. Before we start changing anything, lets first install the necessary package by running yarn add @wordpress/data and remove React Redux by running yarn remove react-redux, as we no longer need it.

Preparing the reducer

First off, we’ll edit the src/reducers/index.js file and change import { combineReducers } from “redux”; to import { combineReducers } from “@wordpress/data”;. That’s all you need to do to prepare your reducers.

Preparing the actions

Due to the way WordPress’ API registers stores and utilizes the actions that go with them, it is necessary to import the actions separately before we’re even be able to register them.

First off, we’ll add an index.js file to the src/actions directory and add the following code:

export { setFormTitle, setFormSlug, setFormContent, setFormExcerpt } from "./form";

This way, we can later tell WordPress what actions we have so it knows what to keep an eye out for!

Creating the selectors

After this, we’ll create a new directory in the src/ directory called selectors. In this directory, we’ll add a file named formData.js and an index.js file for easier importing.

In the formData.js file, add the following code:

/**
 * Gets the title.
 * 
 * @param {Object} state The state to get the title from.
 * 
 * @returns {string} The title.
 */
export function getTitle( state ) {
    return state.form.title;
}

/**
 * Gets the slug.
 *
 * @param {Object} state The state to get the slug from.
 *
 * @returns {string} The slug.
 */
export function getSlug( state ) {
    return state.form.slug;
}

/**
 * Gets the content.
 *
 * @param {Object} state The state to get the content from.
 *
 * @returns {string} The content.
 */
export function getContent( state ) {
    return state.form.content;
}

/**
 * Gets the excerpt.
 *
 * @param {Object} state The state to get the excerpt from.
 *
 * @returns {string} The excerpt.
 */
export function getExcerpt( state ) {
    return state.form.excerpt;
}

In the index.js file that we created, add the following line:

export * from "./formData";

The sole purpose of the aforementioned line of code is to ensure all the functions we defined in our selector are easily importable. Of course, you could refrain from using an index.js file and call import * as formDataSelectors from "./selectors/formData"; from within the forms, but as your codebase might start growing, you’ll end up importing a lot of different selectors and needing to alias them seperately. Our advice is to use the index.js method to ensure you remain future-proof and can scale more easily.

Updating the forms

React

The first thing we’ll need to change in the React version of the form file (simple-form-react.js) are the imports. Replace the imports on lines 2 and 3 with the following:

import { withSelect, withDispatch } from "@wordpress/data";
import { compose } from "@wordpress/compose";

After this, scroll down to the mapStateToProps function and edit it to be the following:

/**
 *  Maps the state to props.
 *
 * @param {Object} state The state to map.
 *
 * @returns {Object} The mapped props.
 */
const mapStateToProps = ( select ) => {
    const store = select( "yoast/api-example" );

    return {
        title:   store.getTitle(),
        slug:    store.getSlug(),
        content: store.getContent(),
        excerpt: store.getExcerpt(),
    };
}

In the previous version of the above code, we were directly calling the properties on the passed state. The WordPress way of things uses a method called select that accesses the store we will be registering later on and allows us to call the functions we defined in our selector's file.

Next thing we’ll need to alter is the mapDispatchToProps function. Replace it with the following code:

/**
 * Maps actions to be dispatched, to props.
 *
 * @param {function} dispatch The dispatch function.
 *
 * @returns {Object} The dispatch functions mapped to props.
 */
const mapDispatchToProps = ( dispatch ) => {
    const store = dispatch( "yoast/api-example" );

    return {
        setFormTitle:   store.setFormTitle,
        setFormSlug:    store.setFormSlug,
        setFormContent: store.setFormContent,
        setFormExcerpt: store.setFormExcerpt,
    }
}

Where we would dispatch methods in the previous version of this function, this one calls upon methods registered within the store, under our own namespace.

Last, but not least for this file, we’ll replace the last line with the following:

export default compose( withSelect( mapStateToProps ), withDispatch( mapDispatchToProps ) )( BaseSimpleForm );

This tells the WordPress API what method to use to get data from and what method to use to write data to the state, which is fairly similar to how we did this with React Redux.

Last, but certainly not least, we’ll have to edit our registration of the store. Open up app.js and replace the code on lines 9 to 12 with the following:

registerStore( "yoast/api-example", {
    reducer: rootReducer,
    selectors,
    actions,
} );

The above code registers the store with WordPress. As mentioned before, actions need to be separately passed along to the store to make WordPress aware of them. Additionally, we also need to pass along the selectors we made earlier on so we can use these to get data from the store.
One extra change that’s necessary for the React version to properly work, is to remove the Provider component from the app.js file that we used to wrap our form with.

That’s it for the React version! Make sure you run yarn build-dev before testing. To see if your data is being persisted to the store, open up the Redux Developer Tool and check the store you registered under its unique namespace.

jQuery

For the jQuery version of the form, we’ll also have to make some adjustments. Open up simple-form.js and replace lines 2 and 3 with the following code to import the necessary libraries and files:

import { dispatch, registerStore, select, subscribe } from "@wordpress/data";
import * as selectors from "./selectors";
import * as actions from "./actions";

const NAMESPACE = "yoast/api-example";

As you might have read earlier, we need change what we include into our files. For the jQuery version of the form, we’ll have to additionally include a dispatch, select and subscribe function from the wordpress/data library on top of the registerStore function. Unlike the React version, there is no simple helper methods to select and dispatch data from and to the store, so we’ll have to add those manually.

The last line in the above code is just a constant we’ll be using so we could (in theory) alter the namespace we use within this file in just one place. This isn’t explicitly necessary for this form to work, but just a matter of preference and a good future proof approach.

Next, we’ll create the store that we’ll be using to save our application state in. Replace the previous instance of the store with the following:

registerStore( NAMESPACE, {
    reducer: rootReducer,
    selectors,
    actions,
} );

Next up, we’ll have to edit the event bindings we’ve written earlier so that they properly dispatch any changes to the newly created store. For every bound event, change the code from store.dispatch( methodName( this.value ) ); to dispatch( NAMESPACE ).methodName( this.value );. If you’ve done this correctly, your event bindings should look as follows:

$( '#title' ).on( 'keyup', function() {
    dispatch( NAMESPACE ).setFormTitle( this.value );
} );

$( '#slug' ).on( 'keyup', function() {
    dispatch( NAMESPACE ).setFormSlug( this.value );
} );

$( '#content' ).on( 'keyup', function() {
    dispatch( NAMESPACE ).setFormContent( this.value );
} );

$( '#excerpt' ).on( 'keyup', function() {
    dispatch( NAMESPACE ).setFormExcerpt( this.value );
} );

The last bit that needs replacing is the way we subscribe to the store. To do this, we’ll be using the subscribe function we’ve imported earlier on. Additionally, we’ll have to call upon our selectors to gather the data from the store. Replace the old store.subscribe code with the following:

subscribe( function() {
    const store = select( NAMESPACE );

    $( '#title' ).val( store.getTitle() );
    $( '#slug' ).val( store.getSlug() );
    $( '#content' ).val( store.getContent() );
    $( '#excerpt' ).val( store.getExcerpt() );
} );

That’s it! Make sure you run yarn build-dev before trying the form out. Again, you can use the Redux Developer Tool to ensure your data is being stored into the store we defined under our own namespace.

Conclusion

That’s it for creating and connecting to WordPress’ stores. We can now safely read and write to our own, isolated store and reference it based on the namespace we defined. In the next post, we’ll explore what other options there are when using the @wordpress/core-data library, which allows us to access WordPress’ own internal data store.