React useEffect组件卸载时输出旧变量值的问题咨询
React useEffect卸载时获取旧状态值的问题解决
问题根源:闭包陷阱
你遇到的是React闭包陷阱导致的问题:
父组件的doSomething函数会在每次父组件渲染时重新创建,它会捕获当前渲染周期的names状态值。而子组件中绑定卸载逻辑的useEffect依赖项是空数组[],意味着它只会在组件挂载时创建一次清理函数,这个清理函数捕获的是父组件初始渲染时传入的doSomething——该版本的函数里names还是初始值BOB,后续哪怕names更新,旧的doSomething里的names不会同步变化,所以卸载时打印的是旧值。
你之前尝试在子组件维护childNames状态的方法之所以失效,是因为每次names变化都会触发childNames更新,导致useEffect依赖项变更,React会在组件重新渲染时先执行上一次的清理函数,最终变成每次输入都触发doSomething,不符合仅卸载时触发的需求。
解决方案
方案1:用useCallback包裹父组件的doSomething
通过useCallback让doSomething仅在names变化时重新创建,同时子组件的useEffect依赖项加上doSomething,确保卸载时调用的是最新版本的函数。
父组件修改:
const ParentComponent = () => { const [names, setNames] = useState('BOB'); const childRef = useRef(null); // 用useCallback包裹,依赖names const doSomething = useCallback(() => { console.log('Did something with the names:' + names) }, [names]); return ( <> <Text>The names: {names}</Text> <ChildComponent doSomething={doSomething} names={names} setNames={setNames} ref={childRef} /> </> ); };
子组件修改:
// Do something on willUnmount React.useEffect(() => { return () => { doSomething(); }; }, [doSomething]); // 新增doSomething作为依赖
方案2:父组件用ref保存最新names
用ref跟踪names的最新值,让doSomething直接读取ref的当前值,无需修改子组件。
父组件修改:
const ParentComponent = () => { const [names, setNames] = useState('BOB'); const namesRef = useRef(names); const childRef = useRef(null); // 同步最新names到ref useEffect(() => { namesRef.current = names; }, [names]); const doSomething = () => { // 读取ref.current获取最新值 console.log('Did something with the names:' + namesRef.current) }; return ( <> <Text>The names: {names}</Text> <ChildComponent doSomething={doSomething} names={names} setNames={setNames} ref={childRef} /> </> ); };
方案3:子组件用ref保存最新names
在子组件内部用ref跟踪names的最新值,卸载时直接传入该值调用doSomething。
子组件修改:
const ChildComponent = forwardRef((props, ref) => { const {doSomething, names, setNames } = props; const latestNamesRef = useRef(names); const textInputRef = useRef(null); // 修复函数组件不能用this的问题 // 同步最新names到ref useEffect(() => { latestNamesRef.current = names; }, [names]); // Load names to input on mount React.useEffect(() => { textInputRef.current?.focus(); textInputRef.current.value = names; }, []); // Do something on willUnmount React.useEffect(() => { return () => { doSomething(latestNamesRef.current); }; }, []); return ( <View> <TextInput value={names} onChangeText={text => setNames(text)} ref={textInputRef} /> </View> ); });
父组件对应修改doSomething:
const doSomething = (latestNames) => { console.log('Did something with the names:' + latestNames) };
内容的提问来源于stack exchange,提问作者Andras Karpati
相关产品推荐
相关产品推荐

