React自定义异步setState Hook失效,await后无法获取更新状态
解决React中useAsyncState await后仍获取旧state的问题
问题背景
想要通过await处理setState的异步操作,确保后续动作在state更新完成后执行。基于React+TypeScript编写了自定义Hook useAsyncState,但调用await setTest2()后,console.log(test2)输出的仍是未更新的旧值。
自定义Hook代码:
import { useState, useRef, useEffect } from 'react'; const useAsyncState = <T>(initialState: T): [state: T, setState: (newState: T) => Promise<unknown>] => { const [state, setState] = useState<T>(initialState); const resolveState = useRef<(updated: T) => void>(); useEffect(() => { if (resolveState.current) { resolveState.current(state); resolveState.current = undefined; } }, [state]); const setAsyncState = (newState: T) => new Promise(resolve => { resolveState.current = resolve; setState(newState); }); return [state, setAsyncState]; }; export default useAsyncState;
使用示例:
const [test2, setTest2] = useAsyncState<string>(''); const onFunc = async () => { await setTest2('新值'); console.log(test2); // 输出旧值 };
问题原因
React的state具有不可变性,且组件在重新渲染前,当前函数作用域内捕获的state变量不会更新。尽管await setTest2()等待了state更新流程完成,但onFunc函数里的test2仍是当前渲染周期的旧值——新的state要等到组件重新渲染后,才会生成对应的新变量。
解决方法
方法1:让setAsyncState返回新的state值
修改自定义Hook,让setAsyncState的Promise直接返回更新后的state值,这样await后就能直接拿到新值:
import { useState, useRef, useEffect } from 'react'; const useAsyncState = <T>(initialState: T): [state: T, setState: (newState: T | ((prev: T) => T)) => Promise<T>] => { const [state, setState] = useState<T>(initialState); const resolveState = useRef<(updated: T) => void>(); useEffect(() => { if (resolveState.current) { resolveState.current(state); resolveState.current = undefined; } }, [state]); const setAsyncState = (newState: T | ((prev: T) => T)) => new Promise<T>(resolve => { setState(prev => { // 同时处理直接传值和函数式更新两种情况 const updatedState = typeof newState === 'function' ? (newState as (prev: T) => T)(prev) : newState; resolve(updatedState); return updatedState; }); }); return [state, setAsyncState]; }; export default useAsyncState;
使用方式调整为:
const [test2, setTest2] = useAsyncState<string>(''); const onFunc = async () => { const updatedState = await setTest2('新值'); console.log(updatedState); // 输出新值 };
方法2:用useEffect监听state变化
如果后续逻辑需要依赖组件渲染后的最新state,可以通过useEffect监听state的变化,在回调中执行逻辑:
const [test2, setTest2] = useAsyncState<string>(''); // 监听test2变化,更新后执行逻辑 useEffect(() => { console.log(test2); // 输出新值 }, [test2]); const onFunc = async () => { await setTest2('新值'); };
内容的提问来源于stack exchange,提问作者고윤태
相关产品推荐
相关产品推荐

