-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Editor: PostTextEditor: Fix case where empty state value defers to prop #9513
Conversation
if ( state.persistedValue !== props.value && ! state.isDirty ) { | ||
const isReceivingNewNonConflictingPropsValue = ( | ||
state.persistedValue !== props.value && | ||
! state.isDirty |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Am I reading the condition wrong or this should drop the !
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the same condition which existed previously. Is the confusion in the naming? I struggled with it 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For me, the conflict exists only if the state is dirty?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh sorry NonConflicting
:)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For me, the conflict exists only if the state is dirty?
Correct, this is testing for "non-conflicting".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm still confused though :)
For me we should always call computeDerivedState
unless the state.isDirty = false
, am I wrong? Can't we simplify all these condition with just isDirty
checks?
// to getDerivedPropsFromState on this state change. | ||
// | ||
// See: getDerivedPropsFromState (hasStoppedEditing) | ||
this.setState( { value: null } ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we can replace this with this.setState( { persistedValue: this.state.value, isDirty: false } );
right? It seems a bit clearer to me but not very important though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we can replace this with this.setState( { persistedValue: this.state.value, isDirty: false } ); right? It seems a bit clearer to me but not very important though.
I don't think we can, or at least not without making the newly-added hasStoppedEditing
effectively dead code (since when would value
then ever be null
)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was actually thinking hasStoppedEditing
is useless because that's the exact purpose of isDirty
value: null, | ||
isDirty: false, | ||
}; | ||
this.state = computeDerivedState( props ); | ||
} | ||
|
||
static getDerivedPropsFromState( props, state ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just realized.... there's no such thing as getDerivedPropsFromState
. There is a getDerivedStateFromProps
. I think this has been dead code all along?
2cf6ae6
to
3b2444e
Compare
The new approach in 3b2444e is much simpler, and leverages |
* @see stopEditing | ||
* | ||
* @param {Event} event Change event. | ||
*/ | ||
edit( event ) { | ||
const value = event.target.value; | ||
this.props.onChange( value ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this is a bit weird :). I suppose this is only needed to trigger the post dirtiness, right? I wonder if a specific action is not better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Granted it's not being changed in this PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you elaborate on what's weird here? onChange
is called to surface the edit to the editor store immediately (so as to mark for dirty detection).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we filling the content
in the edits
? For me the blocks
is the edits
of the content
property. And we're already filling the blocks properly when persisting. I bet this is only needed to trigger a "dirty" state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I bet this is only needed to trigger a "dirty" state.
Pretty much this, yes 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this better than a SET_DIRTY
action or something like that? It's a bit clearer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's an option, one which would contradict the purpose of proposed #7409.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't necessarily agree that SET_DIRTY
is clearer in the broader sense. In practical terms as it impacts what we're doing here, sure, the intent with calling editPost
is to make sure the post becomes marked as dirty, but dirtiness is not really meant to be an explicit action, more a reflection of the current state (diff of edits and saved post). It's by various complexities of content/blocks syncing that we've landed at our current implementation, but I'd rather not further engrain ourselves to this idea of "make dirty" if we can help it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 👍
Extracted from #9403
This pull request seeks to fix a bug with PostTextEditor where intended state value is not always reflected in the rendered textarea when empty. Since previously, it relied on a simple truthy condition to determine whether to use state or props value, if the user had erased all content from the textarea, it would effectively trigger this fallback to props value to occur, thus disregarding the local (user-intended) state.
In practice, this had no effect only because props edit state was kept in sync, so the value remained the same. The issue became more obvious through related refactoring in #9403.
Testing instructions:
There should be no regressions in the behavior of the PostTextEditor.
Ensure unit tests pass:
Future tasks:
In testing this myself, I discovered some unfortunate consequences of our "delayed sync" blocks parsing behavior. Since we rely on the
blur
event to persist content, the sync doesn't always occur, for example when using the keyboard shortcut to switch between modes. I created an issue at #9512 to track (with potential fix included).