React父组件状态变更后如何更新子组件Props?Draft.js状态传递问题
这个场景我太熟悉了!之前在做Draft.js相关的项目时也碰到过类似的状态同步问题,完全不用依赖setTimeout这种hack或者已经被官方废弃的UNSAFE生命周期,React本身就有几个优雅的解决方案,我给你梳理一下:
Draft.js的CustomEditor内部默认会用EditorState.createEmpty()初始化编辑器状态,那你可以在顶层的Notes组件里,也用同样的初始值来初始化state。这样一来,Sidebar从一开始拿到的就是和编辑器一致的有效状态,后续编辑器状态变化时,通过回调更新Notes的state,Sidebar也会实时同步最新值。
举个函数组件的例子:
import { useState } from 'react'; import { EditorState } from 'draft-js'; function Notes() { // 初始化和Draft.js编辑器一致的空状态 const [editorState, setEditorState] = useState(EditorState.createEmpty()); // 接收编辑器状态变化的回调 const handleEditorChange = (newState) => { setEditorState(newState); }; return ( <div className="notes-container"> {/* 把初始状态传给CustomEditor,确保两边起始状态一致 */} <CustomEditor initialEditorState={editorState} onStateChange={handleEditorChange} /> {/* 直接传递同步后的state给Sidebar */} <Sidebar currentEditorState={editorState} /> </div> ); }
如果是类组件,逻辑也是一样的,在constructor里初始化state即可:
import { Component } from 'react'; import { EditorState } from 'draft-js'; class Notes extends Component { constructor(props) { super(props); this.state = { editorState: EditorState.createEmpty() }; } handleEditorChange = (newState) => { this.setState({ editorState: newState }); }; render() { return ( <div className="notes-container"> <CustomEditor initialEditorState={this.state.editorState} onStateChange={this.handleEditorChange} /> <Sidebar currentEditorState={this.state.editorState} /> </div> ); } }
如果你的编辑器初始化逻辑比较特殊(比如从后端加载内容),没法提前同步初始状态,那可以给Sidebar加个渲染条件,只有当editorState真正就绪时才渲染它,或者给它传一个兜底的空状态,避免接收无效值:
function Notes() { const [editorState, setEditorState] = useState(null); const handleEditorChange = (newState) => { setEditorState(newState); }; return ( <div className="notes-container"> <CustomEditor onStateChange={handleEditorChange} /> {/* 只有当editorState存在时才渲染Sidebar */} {editorState && <Sidebar currentEditorState={editorState} />} {/* 或者给Sidebar传兜底的空状态 */} {/* <Sidebar currentEditorState={editorState || EditorState.createEmpty()} /> */} </div> ); }
如果你的组件层级比较深,或者有多个组件需要用到这个编辑器状态,那用React的Context API来管理状态会更省心,不用一层层传递props,也能完美解决同步问题:
首先创建一个编辑器状态的Context:
import { createContext, useContext, useState } from 'react'; import { EditorState } from 'draft-js'; // 创建Context const EditorContext = createContext(); // 封装Provider组件 export function EditorProvider({ children }) { const [editorState, setEditorState] = useState(EditorState.createEmpty()); return ( <EditorContext.Provider value={{ editorState, setEditorState }}> {children} </EditorContext.Provider> ); } // 封装自定义Hook,方便组件使用 export function useEditor() { return useContext(EditorContext); }
然后在Notes组件里用Provider包裹子组件:
function Notes() { return ( <EditorProvider> <CustomEditor /> <Sidebar /> </EditorProvider> ); }
之后CustomEditor和Sidebar就可以直接通过自定义Hook获取状态和更新方法,不用再传递props:
// CustomEditor组件 import { useEditor } from './EditorContext'; function CustomEditor() { const { editorState, setEditorState } = useEditor(); return ( <Editor editorState={editorState} onChange={setEditorState} /> ); } // Sidebar组件 import { useEditor } from './EditorContext'; function Sidebar() { const { editorState } = useEditor(); return ( <div className="sidebar"> {/* 直接使用editorState进行渲染 */} <p>当前内容长度:{editorState.getCurrentContent().getPlainText().length}</p> </div> ); }
这种方式不仅解决了状态同步的问题,还让代码结构更清晰,后期维护也更方便。
总的来说,优先选择方案1(同步初始状态)或者方案3(Context API),都是React官方推荐的做法,完全不用依赖那些不优雅的临时解决方案。
内容的提问来源于stack exchange,提问作者Draxy

