EditorState Race Conditions
Editor is a controlled input component (you can read about this in detail in the API Basics section), meaning that changes made to the
Editor state are propagated upwards through
onChange and it's up to the app to feed it back to the
This cycle usually looks like:
Different browser events can trigger the
Editor to create a new state and call
onChange. For instance, when the user pastes text into it, Draft parses the new content and creates the necessary data structure to represent it.
This cycle works great, however, it is an asynchronous operation because of the
setState call. This introduces a delay between setting the state and rendering the
Editor with the new state. During this time period other JS code can be executed.
Non-atomic operations like this can potentially introduce race conditions.
Here's an example: Suppose you want to remove all the text styles that come from the paste. This can be implemented by listening to the onPaste event and removing all styles from the
However, this won't work as expected. You now have two event handlers that set a new
EditorState in the exact same browser event. Since the event handlers will run one after the other only the last
setState will prevail. Here's how it looks like in the JS timeline:
As you can see, since
setState is an asynchronous operation, the second
setState will override whatever it was set on the first one making the
Editor lose all the contents from the pasted text.
You can observe and explore the race condition in this running example. The example also has logging to highlight the JS timeline so make sure to open the developer tools.
As a rule of thumb avoid having different event handlers for the same event that manipulate the
EditorState. Using setTimeout to run
setState might also land you in the same situation.
Anytime you feel you're “losing state” make sure you're not overriding it before the
Now that you understand the problem, what can you do to avoid it? In general be mindful of where you're getting the
EditorState from. If you're using a local one (stored in
this.state) then there's the potential for it to not be up to date.
To minimize this problem Draft offers the latest
EditorState instance in most of its callback functions. In your code you should use the provided
EditorState instead of your local one to make sure you're basing your changes on the latest one.
Here's a list of supported callbacks on the
handlePastedText(text, html, editorState)
The paste example can then be re-written in a race condition free way by using these methods:
handlePastedText you can implement the paste behavior by yourself.
NOTE: If you need to have this behavior in your Editor, you can achieve it by setting the
stripPastedStyles property to