Draft 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 Editor component.

Draft Editor 是一个完全受控的输入组件。(你能够在 API Basics 这一栏目中了解到详细信息),意味着那些针对 Editor 状态的更改将通过onChange 向上传播,并由应用程序反馈给 Editor 组件。

This cycle usually looks like:

这个周期通常是这样的:

  1. ...
  2. this.onChange = function(editorState) {
  3. this.setState({editorState: editorState});
  4. }
  5. ...
  6. <Editor
  7. editorState={this.state.editorState}
  8. onChange={this.onChange}
  9. placeholder="Enter some text..."
  10. />

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.

不同的浏览器事件可以触发编辑器创建新的状态并调用onChange。例如,当用户粘贴文本时,Draft解析新内容并创建必要的数据结构来表示它。

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.

这个周期可以很好的执行,然而,由于setState调用,它成为了一个异步操作。这在设置状态和使用新状态渲染编辑器之间引入了延迟。在这个时间期间,其他JS代码可以被执行。

image.png

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 EditorState:

这些非原子操作可能会引入竞争条件。这里有个例子:假设您想删除来自粘贴的所有文本的样式。这可以通过监听onPaste事件并从EditorState中删除所有样式来实现:

  1. this.onPaste = function() {
  2. this.setState({
  3. editorState: removeEditorStyles(this.state.editorState),
  4. });
  5. };

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:

然而这可能不能像预期一样执行。你现在有两个事件处理程序,他们在完全相同的浏览器事件中设置新的EditorState。因为事件处理程序会一个接一个的运行,所有只有最后一个setState将会占上风,真正的执行。以下是它在JS时间轴上的样子:
image.png

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.

如您所见,由于setState是一个异步操作,第二个setState将覆盖它在第一个setState上设置的任何内容,从而使编辑器丢失粘贴文本中的所有内容。

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.

在这个运行示例中,您可以观察和探索竞态条件。这个例子还有日志记录来突出JS时间轴,所以一定要打开开发工具。

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 Editor re-rendering.

根据经验,避免为操作EditorState的同一个事件使用不同的事件处理程序。使用setTimeout运行setState可能也会使您处于相同的情况。任何时候,当你觉得你在“丢失状态”时,确保你在编辑器重新渲染之前没有覆盖它。

Best Practices

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 Editor:

既然你已经了解了这个问题,你能做些什么来避免它呢?一般来说,要注意从哪里获得EditorState。如果您使用的是本地的(存储在this.state中),那么它就有可能不是最新的。为了最小化这个问题,Draft在它的大多数回调函数中提供了最新的EditorState实例。在您的代码中,您应该使用提供的EditorState而不是本地的EditorState,以确保您的更改基于最新的EditorState。下面是编辑器支持的回调列表:

  • handleReturn(event, editorState)
  • handleKeyCommand(command, editorState)
  • handleBeforeInput(chars, editorState)
  • handlePastedText(text, html, editorState)

The paste example can then be re-written in a race condition free way by using these methods:

这个粘贴的例子可以通过这些方法在竞态条件自由的方式中被重写:

  1. this.handlePastedText = (text, styles, editorState) => {
  2. this.setState({
  3. editorState: removeEditorStyles(text, editorState),
  4. });
  5. };
  6. //...
  7. <Editor
  8. editorState={this.state.editorState}
  9. onChange={this.onChange}
  10. handlePastedText={this.handlePastedText}
  11. placeholder="Enter some text..."
  12. />;

With handlePastedText you can implement the paste behavior by yourself.

使用handlePastedText,您可以自己实现粘贴行为。

NOTE: If you need to have this behavior in your Editor, you can achieve it by setting the Editor‘s stripPastedStyles property to true.

注意:如果需要在编辑器中实现此行为,可以通过将编辑器的stripPastedStyles属性设置为true来实现。