初始值延迟时,如何触发React组件视图重新渲染?
问题描述
子组件接收父组件传入的obj对象,用obj.aBool初始化本地状态bool,渲染按钮切换该值。但obj.aBool初始为undefined,会在2秒后被异步设置,此时子组件视图不会自动更新,需点击按钮后才显示正确值。需要实现监听obj.aBool的变化,自动触发组件重渲染。
代码示例:
const Child = ({ obj }) => { const [bool, setBool] = useState(obj.aBool); useEffect(() => { obj.aBool = bool; console.log(obj); }, [bool]); const toggle = () => { setBool(!bool); } return <button onClick={toggle}> Value is {bool!=undefined?bool.toString():'...'} </button> }
解决方案
方案1:父组件通过状态管理更新对象(推荐)
React无法感知引用类型的内部属性修改,所以父组件需要将obj存入自身状态,修改时生成新对象触发props更新:
父组件代码:
const Parent = () => { const [obj, setObj] = useState({ aBool: undefined }); // 模拟异步赋值 useEffect(() => { setTimeout(() => { // 生成新对象,而非直接修改原对象 setObj(prev => ({ ...prev, aBool: true })); }, 2000); }, []); return <Child obj={obj} />; }
子组件修改:添加监听obj.aBool的useEffect,同步props变化到本地状态:
const Child = ({ obj }) => { const [bool, setBool] = useState(obj.aBool); // 监听props中obj.aBool的变化,更新本地状态 useEffect(() => { if (obj.aBool !== undefined) { setBool(obj.aBool); } }, [obj.aBool]); useEffect(() => { obj.aBool = bool; console.log(obj); }, [bool]); const toggle = () => { setBool(!bool); } return <button onClick={toggle}> Value is {bool != undefined ? bool.toString() : '...'} </button> }
方案2:子组件监听对象属性变化(父组件无法修改时使用)
通过Object.defineProperty给obj.aBool添加存取器,监听属性变化:
const Child = ({ obj }) => { const [bool, setBool] = useState(obj.aBool); useEffect(() => { let currentValue = obj.aBool; // 定义属性监听 Object.defineProperty(obj, 'aBool', { get() { return currentValue; }, set(newVal) { currentValue = newVal; setBool(newVal); }, configurable: true }); // 清理函数,恢复原始属性 return () => { Object.defineProperty(obj, 'aBool', { value: currentValue, configurable: true, writable: true }); }; }, [obj]); useEffect(() => { obj.aBool = bool; console.log(obj); }, [bool]); const toggle = () => { setBool(!bool); } return <button onClick={toggle}> Value is {bool != undefined ? bool.toString() : '...'} </button> }
方案3:定时器轮询检查(应急方案)
定期检查obj.aBool的变化,适合无法使用前两种方案的场景:
const Child = ({ obj }) => { const [bool, setBool] = useState(obj.aBool); const prevValRef = useRef(obj.aBool); useEffect(() => { const interval = setInterval(() => { if (obj.aBool !== prevValRef.current) { prevValRef.current = obj.aBool; setBool(obj.aBool); } }, 100); // 可根据需求调整轮询间隔 return () => clearInterval(interval); }, [obj]); useEffect(() => { obj.aBool = bool; console.log(obj); }, [bool]); const toggle = () => { setBool(!bool); } return <button onClick={toggle}> Value is {bool != undefined ? bool.toString() : '...'} </button> }
说明
- 方案1是React的标准实践,通过状态管理保证props的可追踪性,代码更易维护。
- 方案2、3属于临时hack手段,可能带来性能损耗或意外副作用,仅在父组件无法修改时使用。
内容的提问来源于stack exchange,提问作者popClingwrap
相关产品推荐
相关产品推荐

