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?
- Here are our npm modules
- Reuse code inside Automattic
- Reuse code by third parties developers
- Maintaining them easily
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:
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:
- First of all PostTitle component is defined in this file
- It uses useRichText call to create an richTextRef with is passed in the H1 element
- useRichText will use useInputAndSelection when defining useMergeRefs
- useInputAndSelection is responsible for adding the html native handlers specially the element.addEventListener( ‘input’, onInput );
- Once we detect the change, the function responsible for making it happen on wordpress/data level is onChange which will run an REGEXP_NEWLINES and thus calling onUpdate which will finally dispatch editPost action from editor store.
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()
.
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:
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:
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:
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:
Architecture docs
After all this investigation and tests, now the architecture docs makes way more sense to me as well as the package relationships.
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.