id: advanced-topics-editorstate-race-conditions

title: EditorState 竞争条件

Draft Editor是一个受控的输入组件(您可以在API基础部分中详细了解此内容),这意味着对Editor状态所做的更改将通过onChange向上传播,并由应用程序将其反馈给Editor组件。

此周期通常如下所示:

  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. />

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

这个循环很好用,但是由于setState调用,它是一个异步操作。 这会在设置状态和使用新状态渲染编辑器之间引入延迟。 在这段时间内,可以执行其他JS代码。

Race condition diagram 1

诸如此类的Non-atomic(非原子)操作可能会导致竞争状况。 这是一个示例:假设您要删除粘贴中的所有文本样式。 这可以通过侦听onPaste事件并从EditorState中删除所有样式来实现:

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

但是,这将无法正常工作。 现在,您有两个事件处理程序,它们在完全相同的浏览器事件中设置了一个新的EditorState。 由于事件处理程序将一个接一个地运行,因此仅最后一个setState为准。 这是JS时间轴中的样子:

Race condition diagram 2

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

您可以在此运行示例中观察和探索竞争条件。 该示例还具有记录日志,以突出显示JS时间轴,因此请确保打开开发人员工具。

根据经验,避免对操纵EditorState的同一事件使用不同的事件处理程序。 使用setTimeout运行setState可能还会使您陷入同一情况。 每当您觉得自己处于“losing state”(丢失状态)时,请确保在重新渲染编辑器之前没有覆盖它。

最佳实践

既然您已经了解了问题,那么该如何解决呢? 通常,请注意从何处获取EditorState。 如果您使用的是本地(存储在this.state中),则有可能不是最新的。 为了最小化此问题,Draft在其大多数回调函数中提供了最新的EditorState实例。 在您的代码中,您应该使用提供的EditorState而不是本地的EditorState,以确保您将更改基于最新的状态。 这是编辑器上支持的回调的列表:

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

然后可以使用以下方法以无竞争条件的方式重写粘贴示例:

  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. />;

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

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