Manipulating Client Data in highly interactive screens in WordPress (essentially the editor page in WordPress pre 5.0.0) is not the easiest task to do for plugin authors. As much as WordPress provides a large number of functions to access and manipulate data server side, it fails at providing a consistent way to access and manipulate data on the client.

And by client side data, I refer to things like:

  • What’s the current post content in the editor? How can I update it programmatically?
  • What’s the currently selected tags/categories?
  • Is a given metabox visible or not?
  • How can I be notified if an image block is inserted into the editor of  if the user switches the post status?

Client-data is also about accessing the WordPress REST API Data client-side:

  • How do I retrieve the latest posts?
  • How do I retrieve the current user’s object?
  • How can I create a new Category?

And it can also encompass plugins data. For example:

  • How do I store/update and access my own plugins client-side data?
  • How do I access and update the content of the Yoast metabox?
  • How do I retrieve the content of ACF metaboxes in a custom ACF plugin?

Often, to address these use-cases, we had to manipulate the DOM directly: Retrieve input’s values directly from the DOM or subscribe to DOM events to ensure we’re notified properly. This often leads to hacky code and code that breaks on WordPress/plugin updates because DOM access is not an official extensibility API.

Gutenberg JavaScript-heavy World

With Gutenberg’s release coming and as we add more and more JavaScript-powered UI to WordPress pages, the need for a consistent solution to manage client-side data is urgent, we can’t rely on DOM access anymore. To address this, Gutenberg introduces the Data Module.

Retrieving Data

One of the first use-cases of the Data module is to retrieve data defined by WordPress. To do so, you need to specify the namespace of the data to call a selector on this namespace.

Here’s how we retrieve the content of the post being edited in a Gutenberg Page.

var content = wp.data.select( 'core/editor' ).getEditedPostAttribute( 'content' );

Notice that the data module API is available in the wp.data global variable. The namespace to access the editor’s data is core/editor. And in each namespace, you can use a list of selectors.

A selector is a simple JavaScript function used to retrieve client-side data. In the example above, the selector being called is getEditedPostAttribute. This selector accepts an argument corresponding to the post attribute to retrieve. If we need to retrieve the title of the post instead of its content, we can do:

var title = wp.data.select( 'core/editor' ).getEditedPostAttribute( 'title' );

Protip: The list of the available selectors/namespaces is not documented properly on WordPress/Gutenberg yet. To see the full list of the core/editor‘s selectors, you can check the selectors file here.

You can also type wp.data.select( 'core/editor' ) in your browser’s console in any Gutenberg page to inspect the full list of the selectors available in this namespace.

Updating Data

So, selectors allow us to retrieve data and similarly, each namespace can defined actions to manipulate data (create/update/remove). For example, to update the title of the post being edited in Gutenberg, you can do:

wp.data.dispatch( 'core/editor' ).editPost( { title: 'My New Title' } );

An action is a function called to update the client-data defined in a given namespace.

Protip: To see the full list of the actions defined by the core/editor namespace, you can check the actions file here.
You can also type wp.data.dispatch(  'core/editor' ) in your browser’s console in any Gutenberg page to inspect the full list of the actions available in this namespace.

Register a custom “namespace”

Plugins can also manage their client state using the data module. To achieve this, they need to register their selectors/actions in addition to a reducer to hold and update the state.

A reducer is a function describing the shape of the initial state and how the state value evolves in response to dispatched actions.

Protip: The Data module is built on-top of the redux library. Learning redux is not required to use it but taking a look at the Redux Docs and its glossary should help you master the data module.

As an example, Let’s register a custom store to keep track of a list of todo items:

// This is the reducer
function reducer( state = [], action ) {
  if ( action.type === 'ADD_TODO' ) {
    return state.concat( [ action.todo ] );
  }

  return state;
}

// These are some selectors
function getTodos( state ) {
  return state;
}

function countTodos( state ) {
  return state.length;
}

// These are the actions
function addTodo( text, done = false ) {
  return {
    type: 'ADD_TODO',
    todo: { text: text, done: done };
  };
}

// Now let's register our custom namespace
var myNamespace = 'my-todos-plugin';
wp.data.registerStore( myNamespace, { 
  reducer: reducer,
  selectors: { getTodos: getTodos, countTodos: countTodos },
  actions: { addTodo: addTodo }
} );

Now that the custom namespace is registered, we can consume this in our code, the same way we did for the core/editor’s store.

// Add a new todo item
wp.data.dispatch( 'my-todos-plugin' ).addTodo( 'Finish writing a blog post about the data module', false );

// Retrieve the list of todos
var countTodos = wp.data.select( 'my-todos-plugin' ).countTodos();

React to changes in the state

Another important use-case for WordPress Plugins often is to react to changes happening in Core Data or other plugins. To address this, the Data Module provides a subscribe function. This function allows registering listeners that get called each time the state is changed.

In the following example, we trigger some random behavior each time a new block is added to the editor:

var currentCount = wp.data.select( 'core/editor' ).getBlockCount();
wp.data.subscribe( function() {
  var newCount = wp.data.select( 'core/editor' ).getBlockCount();
  var hasNewBlocks = newCount > currentCount;
  currentCount = newCount;

  if ( newCount > currentCount ) {
    // A new block has been added, do something
    console.log( 'The new block count is :' + newCount );
  }
} );

Declarative data needs

In addition to a new way to handle data client-side, as WordPress and plugins move towards more JavaScript UIs, the use of React and its wordpress abstraction wp.element is growing.

The WordPress element module allow you to describe UI using functions (or components) taking props and returning an HTML representation of the component.

A simple component displaying an h1 can be written like so:

// You can use JSX instead of wp.element.createElement
var el = wp.element.createElement;

function Title( props ) {
  return el( 'h1', {}, props.title );
}

Very often, these UI components need data to work properly. In the example above, the Title component expects a title prop to display its content in an h1.

Let’s say we’d like to display title component using the title of the post being edited in Gutenberg, we can use the select function explained previously and do something like that:

el( Title, { title: wp.data.select( 'core/editor' ).getEditedPostAttribute( 'title' ) } );

But this approach has some downsides. The value of the title prop can change over time and we’d like to refresh our component once that happens. This means we need to use wp.data.subscribe and rerender each time the title changes.

Fortunately, to avoid having to write this logic for ourselves, The data module provides what in the React community is referred to as a Higher-order component. A Higher-order component is a function that wraps another component and feeds it with props.

Here’s how you provide the title as a prop using the data module:

var EditorPostTitle = wp.data.withSelect( function( select ) {
  return {
    title: select( 'core/editor' ).getEditedPostAttribute( 'title' )
  };
} )( Title );

Now when we render the EditorPostTitle component, it will be automatically refreshed each time the title value changes.

el( EditorPostTitle );

Similarly to how the withSelect Higher-order component provides props to components using selectors, the data module also provides a withDispatch Higher-order component to feed components with actions.

Let’s write an input that can be used to change the value of the Gutenberg’s post title.

var el = wp.element.createElement;
var compose = wp.element.compose;

function TitleInput( props ) {
  return el( 'input', { 
    value: props.title,
    onChange: function( event ) {
      props.onChangeTitle( event.target.value );
    },
  } );
}

var PostTitleInput = compose( [
  wp.data.withSelect( function( select ) {
    return {
      title: select( 'core/editor' ).getEditedPostAttribute( 'title' )
    };
  } ),
  wp.data.withDispatch( function( dispatch ) {
    return {
      onChangeTitle: function( title ) {
        dispatch( 'core/editor' ).editPost( { title: title } );
      }
    };
  } )
] )( TitleInput );

Notice in this example how we’re composing both withSelect to retrieve the current title and withDispatch to provide a onChangeTitle callback the input Component can call to perform the updates.

Protip: Notice the compose function being used in the example, it’s an utility function that allow combining several Higher-order components together. 

Learn more about this functional programming pattern here

 Fetching WordPress Data

Until now, we were mostly dealing with synchronous selectors and actions but WordPress Data offers a way to handle asynchronous data as well.

To do so, we can attach a side-effect to each selector: A function that gets executed the first time a selector is called with a given set of arguments. This function can be responsible of performing an async API request and updating the state once the request succeeds. These side-effect functions are called resolvers.

For instance, we might be interested to fetch posts list to display them in a PostList component.

var el = wp.element.createElement;

// This a UI component showing a list of post titles given a posts prop.
function PostList( props ) {
  return props.posts.map( function( post ) {
    return el( 'div', { key: post.id }, post.title.rendered );
  } );
}

var RecentPostList = wp.data.withSelect( function( select ) {
  posts: select( 'core' ).getEntityRecords( 'postType', 'post' )
} );

Notice that in this example, the APIs being used are the same we were using before, but behind the scenes, a REST API is being executed the first time the getEntityRecords selector is called, to fetch the post list.

Here’s the exact flow:

  • The first time the getEntityRecords selector is called, the post list is empty. So the PostList component is rendered with an empty list of posts.
  • But behind the scenes the core data module also calls the resolver called getEntityRecords to perform the API Request for you. 
  • When the API Request resolves, the state is updated with the received posts. This will automatically retrigger a rerender, thanks to the way withSelect works.
  • The PostList gets rerendered with the update list of posts.  

Protip: If you’re interested in implementing this kind of side effects in your own plugins, make sure to take a look at the resolvers in the Data module docs.

WordPress Headless / Agnostic?

The Data Module is a generic JavaScript package to handle data, if you want to use in a WordPress client, or if you want to use any applications not related with WordPress, that’s also possible. Instead of using the wp.data global, you can just fetch the package from npm:

npm install @wordpress/data --save

Going further

The Data Module is still being enhanced, if you’re Interested in learning more about the Data Module or any other modules, make sure to join the #core-js channel of the WordPress Core slack and the weekly meetings happening each Tuesday at 13:00 UTC.

Thanks Andrew Duthie for the feedback.


This post is brought to you by the new Gutenberg Editor 

2 thoughts on “ Efficient client data management for WordPress Plugins ”

  1. Nice work Riad
    I’ve seen a video which talk about sending data between wordpress and block that could be useful to somebody
    https://www.youtube.com/watch?v=g-y3SXeooyo
    I’ve have a question to ask you about columns
    you use InnerBlocks layouts={ getColumnLayouts( columns ) }
    Is it possible to wrap each of this columns in a component ?
    Something that could be used like this
    InnerBlocks layouts={ [
    { name: ‘column-1’, label: ‘Column 1’, icon: ‘columns’ , wrapper:’Mycomponent’},
    { name: ‘column-2’, label: ‘Column 2’, icon: ‘columns’, wrapper:’Mycomponent’ },
    ] }

    1. I know the columns block is being worked on to add wrappers, so yeah, keep an eye on the next releases.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.