Skip to content

Commit

Permalink
Update 'latestEditorState' in render too
Browse files Browse the repository at this point in the history
Summary:
**tldr;** Handlers can get called after cWU and before cDU, and they use the
cached 'latestEditorState'. For them to get the fresh version of the state,
we need to update 'latestEditorState' in 'render' because that happens before
the handlers get called.

**The whole story;**
We are trying to remove cWU from `DraftEditor.react.js`. One thing which
currently happens there is that `latestEditorState` gets set to the
nextProps.editorState, so whatever is passed in by the parent. For more context
on the initial attempt at changing this, see
https://our.intern.facebook.com/intern/diff/D6873303/

This change introduced one bug that manifested in various ways - see T26034821
and T26020049. I'll focus on T26020049, which broke
`<AdsBulkTokenizedTextInputWithFieldsSelector>`.

`<AdsBulkTok...FieldsSelection>` has a button for adding a new token, and when
you hit that button it passes `<DraftEditor>` an updated editorState as a prop.
This triggers render #1, and during that render we update the browser
selection, which triggers `focus` and `blur` events. These each trigger another
render of `<DraftEditor>`; renders #2 and #3. Here is how it looks on a
timeline:
```
render #1
+
|
| cWU -> latestEditorState = FRESH_STATE
|
| render -> this.props.editorState = FRESH_STATE
| +
| |
| +--> triggers 'focus' event, calling 'handleFocus' with latestEditorState
|                   +
|                   |
+>cDU fires         | the 'handleFocus' call schedules render #2
                    | with latestEditorState, which is FRESH_STATE
                    |
render #2  <--------+
+
|
| cWU -> latestEditorState = FRESH_STATE
|
| render -> this.props.editorState = FRESH_STATE
|
+>cDU fires
```
When we move the update of latestEditorState to cDM, things go awry like this;
```
render #1
+
|
| cWU -> Nothing ... latestEditorState = STALE_STATE :(
|
| render -> this.props.editorState = FRESH_STATE
| +
| |
| +--> triggers 'focus' event, calling 'handleFocus' with latestEditorState
|                                                +
|                                                |
+>cDU -> latestEditorState = FRESH_STATE         | the 'handleFocus' call schedules render #2
                                                 | with latestEditorState, which is STALE_STATE :(
                                                 |
render #2 <--------------------------------------+
+
|
| cWU -> nothing updates
|
| render -> this.props.editorState = STALE_STATE :(  because this was passed in above
|
+>cDU fires and resets latestEditorState = STALE_STATE :(
```

So we can fix things by updating latestEditorState inside the `render` method,
like so;
```
render #1
+
|
| cWU -> Nothing ... latestEditorState = STALE_STATE :(
|
| render -> this.props.editorState = FRESH_STATE
| +         *and* set latestEditorState = FRESH_STATE
  |
| |
| +--> triggers 'focus' event, calling 'handleFocus' with latestEditorState
|                                                +
|                                                |
+>cDU -> latestEditorState = FRESH_STATE         | the 'handleFocus' call schedules render #2
                                                 | with latestEditorState, which is FRESH_STATE
                                                 |
render #2 <--------------------------------------+
+
|
| cWU -> nothing updates
|
| render -> this.props.editorState = FRESH_STATE which was passed in above
|
+>cDU fires and resets latestEditorState = FRESH_STATE
```

One possible issue would be if `render` fired and then was never completed, in
async. mode, but since Draft is intended to always be run in sync. mode I'm not
worried about that.

Reviewed By: mitermayer

Differential Revision: D6994261

fbshipit-source-id: 80986b853a57f64aa5aafbe667b4d94171d5271c
  • Loading branch information
flarnie authored and facebook-github-bot committed Feb 16, 2018
1 parent a6317e6 commit 90a8f22
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 3 deletions.
37 changes: 37 additions & 0 deletions src/component/base/DraftEditor.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,43 @@ class DraftEditor extends React.Component<DraftEditorProps, State> {
}

render(): React.Node {
if (gkx('draft_js_remove_componentwillupdate')) {
/**
* Sometimes a render triggers a 'focus' or other event, and that will
* schedule a second render pass.
* In order to make sure the second render pass gets the latest editor
* state, we update it here.
* Example:
* render #1
* +
* |
* | cWU -> Nothing ... latestEditorState = STALE_STATE :(
* |
* | render -> this.props.editorState = FRESH_STATE
* | + *and* set latestEditorState = FRESH_STATE
* |
* | |
* | +--> triggers 'focus' event, calling 'handleFocus' with latestEditorState
* | +
* | |
* +>cdU -> latestEditorState = FRESH_STATE | the 'handleFocus' call schedules render #2
* | with latestEditorState, which is FRESH_STATE
* |
* render #2 <--------------------------------------+
* +
* |
* | cwU -> nothing updates
* |
* | render -> this.props.editorState = FRESH_STATE which was passed in above
* |
* +>cdU fires and resets latestEditorState = FRESH_STATE
* ---
* Note that if we don't set latestEditorState in 'render' in the above
* diagram, then STALE_STATE gets passed to render #2.
*/

this._latestEditorState = this.props.editorState;
}
const {
blockRenderMap,
blockRendererFn,
Expand Down
6 changes: 3 additions & 3 deletions src/component/contents/DraftEditorLeaf.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,11 @@ class DraftEditorLeaf extends React.Component<Props> {
shouldComponentUpdate(nextProps: Props): boolean {
const leafNode = ReactDOM.findDOMNode(this.leaf);
invariant(leafNode, 'Missing leafNode');
return (
const shouldUpdate =
leafNode.textContent !== nextProps.text ||
nextProps.styleSet !== this.props.styleSet ||
nextProps.forceSelection
);
nextProps.forceSelection;
return shouldUpdate;
}

componentDidUpdate(): void {
Expand Down

0 comments on commit 90a8f22

Please sign in to comment.