React中useState变量在useEffect状态变更后调用函数不更新问题
问题根源:React闭包陷阱
你遇到的问题本质是闭包捕获了旧的状态值。组件首次渲染时,testIfDoneLoading函数捕获了当时的loading状态(true),当你把这个函数存入queuedFunction.current后,即使后续setLoading(false)更新了状态,调用缓存的函数时,它依然使用的是首次渲染时的loading值,导致再次进入暂存逻辑。
解决方案
方案一:用useRef同步状态,获取最新值
通过useRef同步loading状态,确保函数能拿到最新的状态值:
import { useState, useEffect, useRef } from 'react'; export default function App() { const [loading, setLoading] = useState(true); const loadingRef = useRef(loading); // 用ref同步最新loading状态 const queuedFunction = useRef<(() => void) | undefined>(undefined); // 同步state和ref useEffect(() => { loadingRef.current = loading; }, [loading]); useEffect(() => { testIfDoneLoading(); // 模拟异步加载 setTimeout(() => { setLoading(false); }, 1000); }, []); useEffect(() => { if (!loading && queuedFunction.current) { queuedFunction.current(); queuedFunction.current = undefined; } }, [loading]); function testIfDoneLoading() { // 读取ref中的最新loading值 if (loadingRef.current) { console.log('Still Loading'); queuedFunction.current = () => testIfDoneLoading(); } else { console.log('Done Loading!'); } } return <div>{loading ? 'Loading...' : 'Ready'}</div>; }
方案二:直接缓存最终执行逻辑
如果testIfDoneLoading的最终逻辑简单,可以直接缓存目标操作,避免嵌套调用带来的闭包问题:
import { useState, useEffect, useRef } from 'react'; export default function App() { const [loading, setLoading] = useState(true); const queuedAction = useRef<(() => void) | undefined>(undefined); useEffect(() => { testIfDoneLoading(); setTimeout(() => { setLoading(false); }, 1000); }, []); useEffect(() => { if (!loading && queuedAction.current) { queuedAction.current(); queuedAction.current = undefined; } }, [loading]); function testIfDoneLoading() { if (loading) { console.log('Still Loading'); // 直接缓存最终要执行的逻辑 queuedAction.current = () => console.log('Done Loading!'); } else { console.log('Done Loading!'); } } return <div>{loading ? 'Loading...' : 'Ready'}</div>; }
方案三:用useCallback确保函数始终最新
通过useCallback包裹testIfDoneLoading,让它在loading变化时更新,确保缓存的函数能拿到最新状态:
import { useState, useEffect, useRef, useCallback } from 'react'; export default function App() { const [loading, setLoading] = useState(true); const queuedFunction = useRef<(() => void) | undefined>(undefined); // 用useCallback包裹,依赖loading变化时更新函数 const testIfDoneLoading = useCallback(() => { if (loading) { console.log('Still Loading'); queuedFunction.current = () => testIfDoneLoading(); } else { console.log('Done Loading!'); } }, [loading]); useEffect(() => { testIfDoneLoading(); setTimeout(() => { setLoading(false); }, 1000); }, [testIfDoneLoading]); // 依赖更新后的函数 useEffect(() => { if (!loading && queuedFunction.current) { queuedFunction.current(); queuedFunction.current = undefined; } }, [loading]); return <div>{loading ? 'Loading...' : 'Ready'}</div>; }
内容的提问来源于stack exchange,提问作者ian
相关产品推荐
相关产品推荐

