React跨函数调用无法获取useReducer最新state值问题
问题现象
通过useReducer管理分页、排序、筛选相关的TableProps表格属性,表格内交互触发状态变更时TableProps可正常更新,但调用删除逻辑后执行fetch函数时,获取到的TableProps始终为初始值initialProps。逻辑中同时使用createContext向子组件传递状态与方法,所有相关代码均包裹在接收children参数的函数组件内。
原始代码片段:
const [TableProps, dispatch] = useReducer(TableReducer, initialProps); const doDelete = async (id: number) => { setLoading(true); const response: Responsetype = await _delete<Responsetype>(TableProps.fetch + '/' + id, {}); if (response.code) { alert(response); await fetch(); } setLoading(false); } const fetch = () => { console.log(TableProps) // 始终返回初始值 } const context = React.CreateContext(); const value = {doDelete, TableProps .....} return ( <context.provider value={value}> {children} </context.provider> );
问题原因
这是典型的React闭包陷阱:
doDelete、fetch是组件渲染时创建的函数,异步执行删除逻辑的过程中,函数内部捕获的是函数创建时所在渲染周期的TableProps快照,如果后续TableProps更新但函数引用没有同步更新,异步回调里拿到的永远是初始渲染时的旧值。- 直接在Context value中传递每次渲染新生成的普通函数、普通对象,很容易导致子组件拿到旧的函数引用,触发闭包问题。
修复方案
- 用
useCallback包裹操作函数,将用到的状态加入依赖数组,保证状态更新后函数引用同步更新,始终能拿到最新的状态值:
const fetch = useCallback(() => { console.log(TableProps) // 补充列表拉取逻辑 }, [TableProps]) const doDelete = useCallback(async (id: number) => { setLoading(true); const response: Responsetype = await _delete<Responsetype>(TableProps.fetch + '/' + id, {}); if (response.code) { alert(response); await fetch(); } setLoading(false); }, [TableProps, fetch])
- 用
useMemo包裹Context传递的value值,避免每次渲染生成新对象导致子组件异常缓存旧值:
const contextValue = useMemo(() => ({ doDelete, TableProps, dispatch // 其余需要透传的属性、方法 }), [doDelete, TableProps, dispatch]) return ( <context.Provider value={contextValue}> {children} </context.Provider> )
可选方案:如果不想维护hooks依赖,也可以通过useRef存储最新状态,绕开闭包捕获问题:
const tablePropsRef = useRef(TableProps); // 每次状态更新同步到ref useEffect(() => { tablePropsRef.current = TableProps; }, [TableProps]) const fetch = () => { // 直接读ref的current属性永远拿到最新值 console.log(tablePropsRef.current) }
内容的提问来源于stack exchange,提问作者Eugene Pranoto
相关产品推荐
相关产品推荐

