diff --git a/packages/editor/CHANGELOG.md b/packages/editor/CHANGELOG.md index 047ad075220214..8fa4727e06472e 100644 --- a/packages/editor/CHANGELOG.md +++ b/packages/editor/CHANGELOG.md @@ -1,6 +1,6 @@ ## 3.0.0 (Unreleased) -### Breaking Change +### Breaking Changes - The `wideAlign` block supports hook has been removed. Use `alignWide` instead. - `fetchSharedBlocks` action has been removed. Use `fetchReusableBlocks` instead. @@ -18,3 +18,7 @@ ### Deprecation - `wp.editor.RichTextProvider` flagged for deprecation. Please use `wp.data.select( 'core/editor' )` methods instead. + +### Bug Fixes + +- The `PostTextEditor` component will respect its in-progress state edited value, even if the assigned prop value changes. diff --git a/packages/editor/src/components/post-text-editor/index.js b/packages/editor/src/components/post-text-editor/index.js index 5a4121998a60cf..91037ab63a0bc6 100644 --- a/packages/editor/src/components/post-text-editor/index.js +++ b/packages/editor/src/components/post-text-editor/index.js @@ -13,67 +13,59 @@ import { parse } from '@wordpress/blocks'; import { withSelect, withDispatch } from '@wordpress/data'; import { withInstanceId, compose } from '@wordpress/compose'; -/** - * Returns the PostTextEditor state given a set of props. - * - * @param {Object} props Component props. - * - * @return {Object} State object. - */ -function computeDerivedState( props ) { - return { - persistedValue: props.value, - value: props.value, - isDirty: false, - }; -} - -class PostTextEditor extends Component { +export class PostTextEditor extends Component { constructor() { super( ...arguments ); - this.startEditing = this.startEditing.bind( this ); this.edit = this.edit.bind( this ); this.stopEditing = this.stopEditing.bind( this ); - this.state = { - value: null, - isDirty: false, - }; + this.state = {}; } - static getDerivedPropsFromState( props, state ) { - // If we receive a new value while we're editing (but before we've made - // changes), go ahead and clobber the local state - if ( state.persistedValue !== props.value && ! state.isDirty ) { - return computeDerivedState( props ); + static getDerivedStateFromProps( props, state ) { + if ( state.isDirty ) { + return null; } - return null; - } - - startEditing() { - // Copying the post content into local state ensures that edits won't be - // clobbered by changes to global editor state - this.setState( { value: this.props.value } ); + return { + value: props.value, + isDirty: false, + }; } + /** + * Handles a textarea change event to notify the onChange prop callback and + * reflect the new value in the component's own state. This marks the start + * of the user's edits, if not already changed, preventing future props + * changes to value from replacing the rendered value. This is expected to + * be followed by a reset to dirty state via `stopEditing`. + * + * @see stopEditing + * + * @param {Event} event Change event. + */ edit( event ) { const value = event.target.value; this.props.onChange( value ); this.setState( { value, isDirty: true } ); } + /** + * Function called when the user has completed their edits, responsible for + * ensuring that changes, if made, are surfaced to the onPersist prop + * callback and resetting dirty state. + */ stopEditing() { if ( this.state.isDirty ) { this.props.onPersist( this.state.value ); + this.setState( { isDirty: false } ); } - - this.setState( { value: null, isDirty: false } ); } render() { - const { value, placeholder, instanceId } = this.props; + const { value } = this.state; + const { placeholder, instanceId } = this.props; const decodedPlaceholder = decodeEntities( placeholder ); return ( @@ -83,8 +75,7 @@ class PostTextEditor extends Component {