React useEffect卸载时意外引用最新props值问题
问题解决:React useEffect清理函数捕获错误的props值
问题根源
你遇到的是典型的React闭包陷阱,结合可能的引用类型变量特性导致:
useEffect依赖数组为空,effect仅在组件挂载时执行一次,对应的清理函数会捕获挂载时的变量。- 若
accessor是数组、对象这类引用类型,即使挂载时捕获了它的引用,后续该引用的内容被修改时,清理函数打印或使用的会是修改后的最新内容。 - 当组件因
accessor或field变化重新渲染时,旧的清理函数不会更新,仍然引用最初的变量(或引用类型的最新内容),导致卸载时行为不符合预期。
解决方案
方案1:添加正确的依赖项(推荐)
将accessor、field和dispatch加入useEffect的依赖数组,这样每当这些值变化时,React会先执行上一次effect的清理函数,再运行新的effect,确保卸载时执行的清理函数对应组件最后一次渲染的props值:
const Group = ({ children, accessor, disabled = false, }: { children: React.ReactNode accessor: Accessor disabled?: boolean }) => { const dispatch = useEditorDispatch() React.useEffect(() => { if (accessor.length === 1 && field === 'audienceExtension') return const attribute = `-${field}` console.log(`on mount ${accessor}`) dispatch({ type: 'setError', payload: { message: 'is empty', attribute, accessor, }, }) return () => { console.log(`on unmount ${accessor}`) dispatch({ type: 'setError', payload: { message: null, attribute, accessor }, }) } }, [accessor, field, dispatch]) // 补充依赖项 }
方案2:用useRef保存挂载时的快照
如果需要严格使用组件挂载时的accessor值(而非后续更新后的),可以用useRef保存挂载时的变量快照。若accessor是引用类型,记得做拷贝避免后续内容修改影响快照:
const Group = ({ children, accessor, disabled = false, }: { children: React.ReactNode accessor: Accessor disabled?: boolean }) => { const dispatch = useEditorDispatch() // 保存挂载时的accessor快照(引用类型需拷贝) const savedAccessorRef = React.useRef<Accessor>(Array.isArray(accessor) ? [...accessor] : accessor) const savedAttributeRef = React.useRef<string>(`-${field}`) React.useEffect(() => { if (savedAccessorRef.current.length === 1 && field === 'audienceExtension') return console.log(`on mount ${savedAccessorRef.current}`) dispatch({ type: 'setError', payload: { message: 'is empty', attribute: savedAttributeRef.current, accessor: savedAccessorRef.current, }, }) return () => { console.log(`on unmount ${savedAccessorRef.current}`) dispatch({ type: 'setError', payload: { message: null, attribute: savedAttributeRef.current, accessor: savedAccessorRef.current }, }) } }, []) // 保持依赖数组为空 }
内容的提问来源于stack exchange,提问作者Antonio Santoro
相关产品推荐
相关产品推荐

