在React组件中,管理文本输入的焦点是一件棘手的任务。浏览器的focus/blur API是必要的,因此纯粹在render()中,通过声明性的方式设置或者删除焦点往往让人感到笨拙和不正确的,并且这势必在控制焦点状态上带来挑战性的尝试。

考虑到这一点,在Facebook中,我们经常选择在包装文本输入的组件上暴露focus()方法。这打破了声明式的范式,但是也简化了工程师在应用程序中管理焦点行为的工作量。

Editor组件允许这种模式,所以在组件上有公共的focus()方法。这允许你在需要的时候,在高阶组件中使用ref来直接调用focus()方法。

组件中的事件监听器将观察焦点更改,并按照预期通过onChange()方法来传递它们,因此stateDOM将保持正确同步。

Translating container clicks to focus(转换外部容器点击以聚焦输入)

你的高阶组件也许会在一个容器中包裹Editor组件,也许还会为了适配你的应用程序,带上padding的样式。

默认的,如果当一个用户尝试聚焦在这个编辑器的时候,在这个容器里但是在渲染的Editor组件之外点击了,编辑器是不会响应点击事件的。因此,推荐你用一个点击事件监听器在你的容器组件中,然后用上述的focus()方法聚焦到你的编辑器中。

例如,纯文本编辑器示例使用此模式。

  1. <!--
  2. Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
  3. This file provided by Facebook is for non-commercial testing and evaluation
  4. purposes only. Facebook reserves all rights not expressly granted.
  5. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  6. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  7. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  8. FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  9. ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  10. CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  11. -->
  12. <!DOCTYPE html>
  13. <html>
  14. <head>
  15. <meta charset="utf-8" />
  16. <title>Draft • Plain Text Editor</title>
  17. <link rel="stylesheet" href="../../../dist/Draft.css" />
  18. </head>
  19. <body>
  20. <div id="target"></div>
  21. <script src="../../../node_modules/react/umd/react.development.js"></script>
  22. <script src="../../../node_modules/react-dom/umd/react-dom.development.js"></script>
  23. <script src="../../../node_modules/immutable/dist/immutable.js"></script>
  24. <script src="../../../node_modules/es6-shim/es6-shim.js"></script>
  25. <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.js"></script>
  26. <script src="../../../dist/Draft.js"></script>
  27. <script type="text/babel">
  28. 'use strict';
  29. const {Editor, EditorState} = Draft;
  30. class PlainTextEditorExample extends React.Component {
  31. constructor(props) {
  32. super(props);
  33. this.state = {editorState: EditorState.createEmpty()};
  34. this.onChange = (editorState) => this.setState({editorState});
  35. this.logState = () => console.log(this.state.editorState.toJS());
  36. this.setDomEditorRef = ref => this.domEditor = ref;
  37. this.focus = () => this.domEditor.focus();
  38. }
  39. componentDidMount(){
  40. this.domEditor.focus()
  41. }
  42. render() {
  43. return (
  44. <div style={styles.root}>
  45. <div style={styles.editor} onClick={this.focus}>
  46. <Editor
  47. editorState={this.state.editorState}
  48. onChange={this.onChange}
  49. placeholder="Enter some text..."
  50. ref={this.setDomEditorRef}
  51. />
  52. </div>
  53. <input
  54. onClick={this.logState}
  55. style={styles.button}
  56. type="button"
  57. value="Log State"
  58. />
  59. </div>
  60. );
  61. }
  62. }
  63. const styles = {
  64. root: {
  65. fontFamily: '\'Helvetica\', sans-serif',
  66. padding: 20,
  67. width: 600,
  68. },
  69. editor: {
  70. border: '1px solid #ccc',
  71. cursor: 'text',
  72. minHeight: 80,
  73. padding: 10,
  74. },
  75. button: {
  76. marginTop: 10,
  77. textAlign: 'center',
  78. },
  79. };
  80. ReactDOM.render(
  81. <PlainTextEditorExample />,
  82. document.getElementById('target')
  83. );
  84. </script>
  85. </body>
  86. </html>