使用testing-library/react-hooks测试时状态未更新问题排查
React状态未更新问题:自定义Hook与测试的修复方案
核心问题诊断
- Hook代码bug:
addToList函数里的setList写法错误,单个对象不能直接用数组扩展运算符展开,会导致数组结构异常,甚至阻塞状态更新流程 - 测试时序问题:初始渲染时
useEffect里的异步API请求还没完成,就直接调用addToList,此时初始的counter设置(从API返回的1)还没生效,后续更新逻辑被打乱
修复步骤
1. 修复自定义Hook的addToList函数
把错误的数组扩展写法改成正确的单个元素前置:
export const useCounter = (): UseCounterResult => { const session = useSession(); const [list, setList] = useState<Customer[]>([]); const [counter, setCounter] = useState(0); const [limit, setLimit] = useState(10); useEffect(() => { (async () => { const response = await getApiClient(session).listCustomers(); setList(response?.customers || []); setCounter(response.todayCount); setLimit(response.dailyLimit); })(); }, [session]); function addToList(newItem: Customer) { // 修复:单个对象不需要扩展,直接放在数组首位 setList(prevList => [newItem, ...prevList]); setCounter(prevCounter => prevCounter + 1); } return {counter, limit, list, addToList}; };
2. 修复单元测试的时序问题
必须等待初始useEffect的异步请求完成,确保初始状态设置好后,再执行addToList操作:
it.only("should add to list", async () => { // arrange (getApiClient as jest.Mock).mockReturnValue({ listCustomers: async () => ({ customers: [], todayCount: 1, dailyLimit: 5, }), }); // act const { result, unmount, waitForNextUpdate } = renderHook(useCounter); // 等待初始useEffect的异步请求完成,状态更新为API返回的值 await waitForNextUpdate(); // 此时counter已经是1,再执行addToList act(() => { result.current.addToList({ mobileNumber: "John", receiverName: "+61456789123", }); }); // 等待addToList触发的状态更新完成 await waitForNextUpdate(); // assert expect(result.current.counter).toBe(2); unmount(); });
补充说明
- 使用函数式更新(
setCounter(prev => prev +1))的写法是正确的,能确保基于最新状态更新,这个部分不需要修改 - 如果测试中仍有问题,可以用
waitFor替代waitForNextUpdate来等待断言条件满足,但优先保证时序逻辑正确
内容的提问来源于stack exchange,提问作者mehran
相关产品推荐
相关产品推荐

