-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Conversation
d30ac88
to
1df1de9
Compare
Note to self: need to at least fix the private props here because they're broken by the class transform internally. |
Looks like this also doesn't quite work in Firefox yet, though it seems no worse than the current code. |
About half done reviewing this, and wondering about your comment "Note to self: need to at least fix the private props here because they're broken by the class transform internally." What is that about, and is more work needed on this before it's ready to merge? Notes so far:
|
In the Facebook web codebase, we transform
We actually already don't support that: https://facebook.github.io/draft-js/docs/advanced-topics-issues-and-pitfalls.html#delayed-state-updates. This change probably brings us closer to being able to support that if we really wanted, actually.
Hmm, maybe related to delegation? https://en.wikipedia.org/wiki/Delegation_pattern |
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.
Most of these changes are just mechanical .props.editorState
-> ._latestEditorState
changes. Just adding ._latestEditorState
in DraftEditor and the changes in the composition handler are the real changes here.
); | ||
// Allow composition handler to interpret the compositionstart event | ||
this._onCompositionStart(e); |
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 was necessary so stillComposing
in the composition handler gets set to true when first entering composition mode.
If you type Option-E, Backspace, then the in-progress accented character should be deleted. Similarly, in 2-Set Korean, if you press A, Backspace, the character should be deleted. Both of these don't work correctly yet, but this commit fixes it. Currently, when you press Backspace, we are in composition mode and we receive a compositionend event then a keydown event for the backspace. Our composition handler doesn't exist composition mode immediately but rather waits 20ms to see if it gets a compositionstart event immediately (and if so, does not exit). It seems like if we receive a compositionend event then a keydown, we always want to exit composition mode immediately and re-interpret the keydown in edit mode. Here I do so. This change is made trickier by the fact that each event handler reads from `this.props.editorState`, which doesn't get updated until the next rerender (that is, not synchronously). To combat this, we store the most current editor state in an instance variable and refer to it from all of the event handlers. (If we don't do this then the backspace isn't interpreted correctly by editOnKeyDown because the character data from compositionend hasn't been inserted into the editor state, so we'd end up effectively dropping the entire composition as well as deleting the preceding character.) In this commit, only editOnKeyDown needs the latest state but I changed the others for consistency. In cases where the parent always updates editorState in the same tick (as we recommend in the docs), there should be no behavior change. Note: This also brings us closer to being able to handle cases where we don't always receive editorState synchronously -- instead we could store our own copy of the editorState locally but overwrite it with props when we receive new ones. It doesn't work yet though. Test Plan: In plaintext example, type Option-E, Backspace (with US keyboard) or A, O, Backspace, Backspace (with 2-Set Korean keyboard). In both cases, the entire composition is deleted correctly.
I decided to go with just adding |
Thanks - I'll try to read the rest of this later today. |
Cool - so it's more like this:
Overall I think this makes sense, and when I manually tested it (briefly) it works. I'm working on the internal sync of 0.10.0, which includes a big API breaking change. I can either add this to 0.10.0 or do a 0.9.2 release. Either way I think it would be great to get this bug fix out soon. Thanks for working on it! |
Yes, that's basically right! It's that we basically wanted two successive events with nothing in between and before we weren't getting the new editorState when processing the second event, because state updates are batched and we hadn't rerendered. |
I can't describe entirely why the DraftEditor.react change here is necessary, but this is closer to what we had before facebookarchive#666. The editOnSelect change fixes holding down Backspace in Firefox -- before this, the cursor would jump to the start of the input.
I can't describe entirely why the DraftEditor.react change here is necessary, but this is closer to what we had before #666. The editOnSelect change fixes holding down Backspace in Firefox -- before this, the cursor would jump to the start of the input.
@spicyj Thanks you for your PR and I'm expecting merged version will be released soon (Because I'm using Korean.) And I wonder another similar problem is solved or not with this PR. The problem is Left and Right arrow key doesn't work properly with Korean IME because of following codes at 0.9.1 https://github.com/facebook/draft-js/blob/master/src/component/handlers/composition/DraftEditorCompositionHandler.js#L96-L98 When I press arrow key, normally I expect to move cursor but it doesn't move and just change editor mode to composition end. Unlike Japanese or Chinese, Korean IME doesn't use arrow key to select or finish composition. In Korean (same as English), arrow keys are used as arrow key whether IME is in composition or not. @spicyj Cloud you test your PR also solve this problem or not? |
Yes, it should. This Wednesday you should be able to confirm that typing works correctly on facebook.com. If not, please file a new issue and tag me in it describing exactly which sequence of keys behaves incorrectly, with your OS and browser version. |
is it applied to npm? if it is applied, I think 2-set korean removing still has problem. 😢 |
No, it is not yet. |
too bad. But I really appreciate your work. I'll wait for the update. |
Sorry @spicyj I'm new to Coding. Does this solve the Enter key problem I have to Enter twice to go to the new line? the first time to trigger compositionend. thx in advance |
Yes, it should. |
thank you I'll test it 👍 you response so fast |
* Remove unused render guard logic * Fix dead key deletion If you type Option-E, Backspace, then the in-progress accented character should be deleted. Similarly, in 2-Set Korean, if you press A, Backspace, the character should be deleted. Both of these don't work correctly yet, but this commit fixes it. Currently, when you press Backspace, we are in composition mode and we receive a compositionend event then a keydown event for the backspace. Our composition handler doesn't exist composition mode immediately but rather waits 20ms to see if it gets a compositionstart event immediately (and if so, does not exit). It seems like if we receive a compositionend event then a keydown, we always want to exit composition mode immediately and re-interpret the keydown in edit mode. Here I do so. This change is made trickier by the fact that each event handler reads from `this.props.editorState`, which doesn't get updated until the next rerender (that is, not synchronously). To combat this, we store the most current editor state in an instance variable and refer to it from all of the event handlers. (If we don't do this then the backspace isn't interpreted correctly by editOnKeyDown because the character data from compositionend hasn't been inserted into the editor state, so we'd end up effectively dropping the entire composition as well as deleting the preceding character.) In this commit, only editOnKeyDown needs the latest state but I changed the others for consistency. In cases where the parent always updates editorState in the same tick (as we recommend in the docs), there should be no behavior change. Note: This also brings us closer to being able to handle cases where we don't always receive editorState synchronously -- instead we could store our own copy of the editorState locally but overwrite it with props when we receive new ones. It doesn't work yet though. Test Plan: In plaintext example, type Option-E, Backspace (with US keyboard) or A, O, Backspace, Backspace (with 2-Set Korean keyboard). In both cases, the entire composition is deleted correctly.
I can't describe entirely why the DraftEditor.react change here is necessary, but this is closer to what we had before facebookarchive#666. The editOnSelect change fixes holding down Backspace in Firefox -- before this, the cursor would jump to the start of the input.
Summary: The last thing we do inside 'componentWillUpdate' in DraftEditor is update the '_latestEditorState'. This was introduced in #666 and that PR has a great explanation in the comments. **BUT** when manually testing this, the bug with dead key deletion and korean IME deletion not working was still happening, even with the current state of things. So that was interesting. It doesn't seem to fix the thing we originally intended, unless I'm missing something. In a later change (https://our.intern.facebook.com/intern/diff/D4778712/) passes latestEditorState to handlers, so that they can use the latest editor state instead of stale editor state. This fixed many bugs in our own uses of Draft.js. And since there are many untested IME cases, it could be that some situations are still improved by storing and tracking the '_latestEditorState'. We already set '_latestEditorState' inside 'update' and that is always called before 'componentWillUpdate'. My first approach is to just rely on that and remove the updating in 'componentWillUpdate'. Updating '_latestEditorState' in 'componentWillUpdate' does potentially give us important info though; if the 'onChange' handler mutated the editorState then we will need to use that, and we get it in 'componentWillUpdate'. If we want to maintain this, then I propose the following; - Move '_latestEditorState' into state in 'DraftEditor' and create a public getter method to access it. - Update it in the new static method 'getDerivedStateFromProps' and then use the polyfill wrapper to make Draft.js backwards compatible. I'd rather skip that if it turns out we don't rely on '_latestEditorState' being updated after 'onChange' is called. With so many uses and so few tests, the only way to know is to try it. :) Follow-up action items: - Add a unit test for latestEditorState being used and passed to handlers. - Open the GK to 70% employees and let that sit for a couple of days, to verify that this doesn't cause major problems. Continue roll-out and remove GK. - Add inline comments explaining why we track and use '_latestEditorState'. Depends on D6866079 Reviewed By: sophiebits Differential Revision: D6873303 fbshipit-source-id: 5df00598ea20d826f96d70eddcfe7043cf18a18b
…on events. It was introduced by facebookarchive#666
If you type Option-E, Backspace, then the in-progress accented character should be deleted. Similarly, in 2-Set Korean, if you press A, Backspace, the character should be deleted. Both of these don't work correctly yet, but this commit fixes it.
Currently, when you press Backspace, we are in composition mode and we receive a compositionend event then a keydown event for the backspace. Our composition handler doesn't exist composition mode immediately but rather waits 20ms to see if it gets a compositionstart event immediately (and if so, does not exit). It seems like if we receive a compositionend event then a keydown, we always want to exit composition mode immediately and re-interpret the keydown in edit mode. Here I do so.
This change is made trickier by the fact that each event handler reads from
this.props.editorState
, which doesn't get updated until the next rerender (that is, not synchronously). To combat this, we store the most current editor state in an instance variable and refer to it from all of the event handlers. (If we don't do this then the backspace isn't interpreted correctly by editOnKeyDown because the character data from compositionend hasn't been inserted into the editor state, so we'd end up effectively dropping the entire composition as well as deleting the preceding character.) In this commit, only editOnKeyDown needs the latest state but I changed the others for consistency. In cases where the parent always updates editorState in the same tick (as we recommend in the docs), there should be no behavior change.Note: This also brings us closer to being able to handle cases where we don't always receive editorState synchronously -- instead we could store our own copy of the editorState locally but overwrite it with props when we receive new ones. It doesn't work yet though.
Test Plan: In plaintext example, type Option-E, Backspace (with US keyboard) or A, O, Backspace, Backspace (with 2-Set Korean keyboard). In both cases, the entire composition is deleted correctly.
cc @hellendag