I'm extending blocks to conditionally add a notice, using wp.hooks.addFilter( 'blocks.BlockEdit'… ), for example:
wp.hooks.addFilter(
'blocks.BlockEdit',
'example/add-notice',
function( BlockEdit ) {
var NewBlockEdit = function( props ) {
return [
wp.element.createElement(
wp.components.Notice,
{
status: 'info',
isDismissible: false,
key: 'example-notice'
},
'This is an example notice.'
),
wp.element.createElement(
BlockEdit,
_.extend( {}, props, { key: 'example-original-edit' } )
)
];
};
return NewBlockEdit;
}
);
In doing this, I've found that when a block uses the RichText component that the overall editor state will become initially dirty after first loading the page, if the component doesn't in fact error due to TinyMCE not being instantiated. When using a production build, _normally_ the editor prop is not initialized yet here and an error is raised:
Even when the editor is initialized, however, the result of calling onChange is that the editor is put into a dirty state, even when no changes have been made by the user. With the above wp.hooks.addFilter( 'blocks.BlockEdit'… ) call in place, if the editor is loaded with a paragraph block, then if you then try to reload the page after making no edits then you'll get prompted with the onbeforeunload “Are you sure?” prompt, as well as the Update button is initially enabled.
In digging through the stack trace it seems the reason is due to the withFilters HOC call to forceUpdate after applying filters:
Down the stack trace this then results in the RichEdit component getting unmounted and componentWillUnmount is called which in turn unconditionally calls onChange even when there are no edits:
addFilter code to a plugin.Check if this.editor is defined and in a dirty state itself before calling onChange during unmounting?
--- a/blocks/rich-text/index.js
+++ b/blocks/rich-text/index.js
@@ -700,7 +700,9 @@ export class RichText extends Component {
}
componentWillUnmount() {
- this.onChange();
+ if ( this.editor && this.editor.isDirty() ) {
+ this.onChange();
+ }
}
componentDidUpdate( prevProps ) {
I can confirm the issue with unnecessary this.onChange(); calls executed every time I register or unregister a filter. Every RichText dispatches an action when it happens even when there were no changes introduced.
Down the stack trace this then results in the RichEdit component getting unmounted and componentWillUnmount is called which in turn unconditionally calls onChange even when there are no edits:
I can confirm that the proposed change fixes this issue. I don't see any reason why onChange would have to be called in the case where there were no changes introduced by the user. @iseulde or @youknowriad can you confirm that it is the proper way of preventing the unwanted Redux state update?
I haven't tested but this issue I see is that we don't "save" the editor when it's not dirty anymore (when we call updateContent or onChange. So this won't work consistently IMO
Also not a fan of adding this.editor.isDirty(). If want to check if the editor is initialized, you can check editor.initialized? Is that what you're wanting to do?
Also not sure if the onChange is really needed on unmount... Everything should be synced whenever there is a change in the editor. At first sight I think it is fine to remove. It seems to be a remnant from when we only synced the value on blur.
Also not sure if the onChange is really needed on unmount... Everything should be synced whenever there is a change in the editor. At first sight I think it is fine to remove. It seems to be a remnant from when we only synced the value on blur.
I agree with this statement, but I'd test carefully the different usecases, of splitting/merging/switching etc... :) fast typing + splitting