使用useRef结合useImperativeHandle时React不重新渲染的问题
问题原因
你当前的问题核心在于:App组件里的isCodeViewMode仅在初始渲染时通过ref获取一次值,之后MenuBar内部的状态更新不会触发App组件重渲染,导致App里的这个值始终停留在初始状态,无法同步MenuBar的最新变化。另外,useRef的current属性变更不会触发组件重渲染,就算后续调用getIsCodeViewMode,App也不会重新计算这个值。
解决方案
方案1:状态提升(最推荐)
把isCodeViewMode的状态从MenuBar移到App组件中,通过props将状态和更新方法传给MenuBar,这样状态变化会直接触发App重渲染,代码视图的类名也能实时更新。
修改App组件:
import { useState } from 'react'; // App组件内定义状态 const [isCodeViewMode, setIsCodeViewMode] = useState(false); return ( <div id="editor-container" className="ps-relative s-textarea p0 d-flex fd-column" style={{ width: "1500px", zIndex: 10 }} > <div className="js-sticky py6 bg-inherit btr-sm w100 ps-sticky t0 l0 z-nav s-editor-shadow js-plugin-container js-sticky"> {/* 将状态和更新方法传入MenuBar */} <MenuBar editor={editor as Editor} isCodeViewMode={isCodeViewMode} setIsCodeViewMode={setIsCodeViewMode} /> </div> <div id="editor-content" className="overflow-scroll fl-grow1 outline-none p12 pt6 w100 s-prose js-editor ProseMirror" > <EditorContent editor={editor} /> </div> <div className={`mw-editor__codemirror ${isCodeViewMode ? "" : "d-none"}`} > <textarea></textarea> </div> </div> );
修改MenuBar组件:
移除内部的useState和useImperativeHandle,直接使用props传递的值和方法:
interface MenuBarProps { editor: Editor; isCodeViewMode: boolean; setIsCodeViewMode: (value: boolean) => void; } const MenuBar: React.FC<MenuBarProps> = ({ editor, isCodeViewMode, setIsCodeViewMode }) => { // 切换视图时直接调用props中的更新方法 const toggleCodeView = () => { setIsCodeViewMode(!isCodeViewMode); }; return ( // 你的MenuBar结构,按钮点击绑定toggleCodeView <button onClick={toggleCodeView}> {isCodeViewMode ? "切换到富文本视图" : "切换到代码视图"} </button> ); };
方案2:回调函数同步状态(需保留MenuBar内部状态时使用)
如果必须把状态留在MenuBar内,可以让MenuBar接收一个回调函数,当内部状态变化时调用该回调,通知App更新自身状态:
修改App组件:
import { useState } from 'react'; const [isCodeViewMode, setIsCodeViewMode] = useState(false); // 定义回调函数,接收MenuBar传来的最新状态 const handleCodeViewChange = (newMode: boolean) => { setIsCodeViewMode(newMode); }; return ( <div id="editor-container" className="ps-relative s-textarea p0 d-flex fd-column" style={{ width: "1500px", zIndex: 10 }} > <div className="js-sticky py6 bg-inherit btr-sm w100 ps-sticky t0 l0 z-nav s-editor-shadow js-plugin-container js-sticky"> <MenuBar editor={editor as Editor} onCodeViewChange={handleCodeViewChange} /> </div> <div id="editor-content" className="overflow-scroll fl-grow1 outline-none p12 pt6 w100 s-prose js-editor ProseMirror" > <EditorContent editor={editor} /> </div> <div className={`mw-editor__codemirror ${isCodeViewMode ? "" : "d-none"}`} > <textarea></textarea> </div> </div> );
修改MenuBar组件:
interface MenuBarProps { editor: Editor; onCodeViewChange: (mode: boolean) => void; } const MenuBar: React.FC<MenuBarProps> = ({ editor, onCodeViewChange }) => { const [isCodeViewMode, setIsCodeViewMode] = useState(false); const toggleCodeView = () => { const newMode = !isCodeViewMode; setIsCodeViewMode(newMode); // 调用回调通知App更新状态 onCodeViewChange(newMode); }; // 若其他场景仍需要ref暴露方法,可保留useImperativeHandle // useImperativeHandle(ref, () => ({ ... }), [isCodeViewMode]); return ( <button onClick={toggleCodeView}> {isCodeViewMode ? "切换到富文本视图" : "切换到代码视图"} </button> ); };
原方案失效的核心原因
App组件中isCodeViewMode = menuBarRef.current?.getIsCodeViewMode()这行代码,仅在组件每次渲染时执行一次。而MenuBar内部状态变化不会主动触发App重渲染,因此App不会重新执行这行代码去获取最新值,自然无法同步状态。
内容的提问来源于stack exchange,提问作者Mercer
相关产品推荐
相关产品推荐

