更新React组件state导致Jest+React Testing Library测试无限运行如何解决
问题产生原因
- 核心诱因是你使用的自定义fetch hook返回的
response对象引用每次渲染都在变化,触发了第二个依赖为response的useEffect反复执行,进而调用setData更新state导致组件无限重渲染。 - 初始值为空白对象
{}时才出现问题的原因:空对象和接口返回的response.data结构不匹配,React的state更新会判定为需要重渲染;而你使用带字段的dummy对象作为初始值时,和mock返回的response.data结构一致,React做浅比较后判定state没有变化,不会触发重渲染,自然打断了无限循环。 - 传入基础类型值不会出问题的原因:布尔、字符串、数字这类基础类型是按值比较的,
setData传入相同值时React会直接跳过state更新,不会触发重渲染,也就不会进入循环。 - 测试环境下问题会被放大的原因:Jest默认会监控所有异步任务直到全部执行完成,无限重渲染会持续产生新的微任务/宏任务,导致测试永远无法进入空闲状态终止运行。
解决方案
1. 修复useEffect的依赖和更新逻辑
- 给依赖为
response的useEffect加前置判断,只有response.data和当前data不相等时才执行更新,避免无效state更新:
useEffect(()=>{ // 浅结构可以用JSON.stringify比较,深结构可以用深比较方法或者Lodash的isEqual if(JSON.stringify(data) !== JSON.stringify(response.data)) { setData(response.data) } },[response, data])
- 如果只需要拿接口返回的data赋值,可以直接把依赖改成
response.data,减少不必要的触发:
useEffect(()=>{ setData(response.data) },[response.data])
2. 优化自定义fetch hook的返回值
在自定义hook内部缓存response对象,只有当接口返回内容实际变化时才更新response的引用,避免每次渲染都生成新的response对象。
3. 测试侧补全异步等待逻辑
你的测试代码目前没有等待异步接口调用完成的逻辑,补全waitFor或者findBy查询(本身自带等待效果),让测试主动等待异步任务执行完成:
// 示例:等待接口返回后渲染的对应元素出现 await findByTestId('data-rendered-element')
内容的提问来源于stack exchange,提问作者user3763367
相关产品推荐
相关产品推荐

