测试时React状态更新未触发重渲染,Material UI开关状态不同步问题
问题根因
- 等待逻辑不可靠:
setTimeout(resolve, 0)仅能清空当前宏任务队列,无法覆盖Apollo Client mutation执行、缓存更新、React重渲染的全链路异步流程,断言执行时组件往往还未完成状态更新。 - Apollo缓存未同步:默认情况下Apollo Client不会自动将mutation返回的结果同步到原有query的缓存中,组件从query获取的
callbackEnabled始终为初始的true,因此不会触发重渲染更新开关状态。本地运行正常通常是因为生产逻辑中触发了query重取、或者配置了缓存更新规则,测试环境仅存在mock请求因此无法触发对应逻辑。 - 冗余的act包裹:react-testing-library的
render、userEvent.click等API已经内置了act处理,额外嵌套act反而可能导致状态更新捕获异常。
修复方案
1. 组件侧补全mutation缓存更新逻辑
在组件调用useMutation时新增update配置,手动将mutation返回的最新值同步到对应query的缓存中:
const [updateCallbackStatus] = useMutation(myMutation, { update(cache, { data: { myMutation } }) { cache.writeQuery({ query: myQuery, variables: { clientId: 'cl_0' }, data: { myQuery: { // 如果组件逻辑开启了typename则补充对应类型字段,否则可省略 callbackEnabled: myMutation.callbackEnabled } } }) } })
2. 测试代码替换不可靠的延时逻辑
使用react-testing-library提供的findBy*查询和waitFor工具等待状态更新,无需手动设置延时:
it('should update boolean field', async () => { const mocks = [ { request: { query: myQuery, variables: { clientId: 'cl_0', }, }, result: { data: { myQuery: { callbackEnabled: true }, }, }, }, { request: { query: myMutation, variables: { clientId: 'cl_0', callbackEnabled: false, }, }, result: { data: { myMutation: { callbackEnabled: false, }, }, }, }, ]; render( <MockedProvider mocks={mocks} addTypename={false}> <MyComponent clientId="cl_0" error={undefined} /> </MockedProvider> ); // 等待初始查询完成,验证初始状态 const callbackSwitch = await screen.findByRole('checkbox', { name: /callbacks/i }); expect(callbackSwitch).toBeChecked(); // 模拟点击开关 userEvent.click(callbackSwitch); // 等待开关状态更新完成后断言 await waitFor(() => { expect(screen.getByRole('checkbox', { name: /callbacks/i })).not.toBeChecked(); }); });
常见疑问
不需要手动强制触发组件重渲染,当前问题本质是组件依赖的状态(Apollo查询结果)未发生变化,因此不会触发重渲染,补全缓存更新逻辑后即可正常触发重渲染。
内容的提问来源于stack exchange,提问作者ege
相关产品推荐
相关产品推荐

