Understanding how Gutenberg saves a Post

In my previous post, I learned how does Gutenberg load posts from our backend. Now, it is time to deeply understand from the bottom up how we actually save a post in Gutenberg.

TLDR;

Focusing on saving a post Title: Inside @wordpress/edit-post package, Layout loads VisualEditor component that loads PostTitle component (from @wordpress/editor package) which will dispatch editPost action containing the new title attribute once user changes it.

editPost action will dispatch a editEntityRecord action from @wordpress/core-data package, and editEntityRecord will dispatch a new action to EDIT_ENTITY_RECORD containing the new state and the undo commands which will be applied into the state by the appropriate reducer.

Since we have an AutosaveMonitor running, it will regularly check for changes in our data and ensure we have, in both locally and remotely, the most updated version of that post by triggering the autosave action.

Once users hit Update button in the editor, we save it definitely /wp/v2/posts endpoint called by the savePost action.

Multiple packages inside Gutenberg

It surprised me when I first saw how many packages do we use inside Gutenberg, and soon I realized it’s driven by packages by default. And the best: we can use them standalone like the component package and also play with in Gutenberg’s Storybook.

Why is it important to have packages?

How does a post title update works?

Once we load a post, on the top of its structure, we have the PostTitle element which is a component that belongs to the @wordpress/editor package and is loaded by @wordpress/edit-post package at the visual-editor component which is loaded by Layout component:

VisualEditor is loaded inside Layout component from edit-post package
PostTitle is loaded inside VisualEditor component from edit-post package

By reading the architecture modularity doc, we can see that @wordpress/edit-post is the implementation of the “New Post” (“Edit Post”) screen in the WordPress admin. It is responsible for the layout of the various components provided by @wordpress/editor and @wordpress/block-editor, with full awareness of how it is presented in the specific screen in the WordPress administrative dashboard.

About the PostTitle component behavior:

Manual update of a post title

We can trigger a post-title update by dispatching the editPost action in our console: wp.data.dispatch( 'core/editor' ).editPost( { title: 'new title ' } );. This will save the title in the local state, for pushing it into our database, we also need to run the savePost action by dispatching: wp.data.dispatch( 'core/editor' ).savePost().

Dispatching editPost action from core-editor package will update the post title

After editPost action being called inside PostTitle component which belongs to editor package, it will perform a dispatch to editEntityRecord action inside @wordpress/core-data package.

CoreData is the data module intended to simplify access to and manipulation of core WordPress entities.

What does editEntityRecord do?

The editEntityRecord action of core-data package, compares the past change with the new one and clears any duplicated edit to avoid meaningless requests and data being saved.

More than comparing it, editEntityRecord also defines what’s are the current edits and the previous ones for the undo/redo system:

Current and previous (undo) edits for a given post change

Once we have the current, and the next command, we dispatch those data by creating an EDIT_ENTITY_RECORD action which will be handled by the Higher Order entity Reducer from the core-data package.

Inside the entity function, we can see how the state was before and after being changed by it after the EDIT_ENTITY_RECORD is performed:

Title changed reflected on state

This means our change is saved in the local state. If we want that in our server, we’ll want to do a simple call in the savePost action by typing: wp.data.dispatch( 'core/editor' ).savePost()

A bit about changing the title and the undo/redo state

We can easily see that record being saved and added to the undo/redo system by using the command wp.data.select( 'core' ).getAllUndoState():

Note: getAllUndoState isn’t a native Gutenberg function, to use that, I first added this code in the selectors.ts:

On my first research about undo/redo system, I was looking for where do we set the state.undo variable and now I know, this is the place where it’s updated.

Autosave Monitor

We have a local and remote handlers for the autosave monitor, a component from @wordpress/editor package.

Saving locally is a great strategy to never losing data, even when we’re offline. AutosaveMonitor has a simple implementation and can be triggered by the following command:

wp.data.dispatch( 'core/editor' ).autosave( { local: true|false
)

The localAutosave is as simpler as using window.sessionStorage.setItem.

The remoteAutosave will trigger the savePost action from editor/store and inside of it, we’ll trigger the saveEntityRecord action from the core store and that will finally make the POST request with the post data containing the modifications at the /wp/v2/posts/1/autosaves endpoint.

In the backend, the /wp/v2/posts/1/autosaves endpoint is handled by the wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php file:

/wp/v2/posts/1/autosaves endpoint file in our backend API

Saving a POST with the Update button

The PostPublishButton from @wordpress/editor package is loaded in the editor header. Once the user clicks on it, we dispatch the savePost action which we can easily simulate with the command : wp.data.dispatch( 'core/editor' ).savePost().

That will also trigger the editEntityRecord and to a similar path that we saw during the title saving above, but this time, it will be litened by the /wp/v2/posts/1 endpoint which is handled by wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php file:

/wp/v2/posts/1 endpoint file in our backend API

Architecture docs

After all this investigation and tests, now the architecture docs makes way more sense to me as well as the package relationships.

Main packages of Gutenberg editor flow

Conclusion

I’m super excited to understanding more and more about Gutenberg, specially because now fewer things sound “magic” and started to sound more like complex well done systems. I’m looking forward to continue my studies, and the next chapter will probably be a deeper look inside wordpress/data package and deeply testing our undo/redo system.

Leave a Reply