Enzyme中延迟执行setState后useEffect钩子未触发的原因?
问题:Promise回调中执行setTest后,监听test的useEffect未输出日志
组件代码
function Test() { const [test, setTest] = useState("init"); const testing = useCallback(() => { console.log("testing"); new Promise(r => r()) .then(() => { console.log("testing 2"); setTest(1); }); }, []); useEffect(() => { testing(); }, [testing]); useEffect(() => { console.log("test has changed", test); }, [test]); return ( <div>{test}</div> ); }
测试代码
test.only("a", async () => { let wrapper = mount(<Test />); console.log(wrapper.debug()); await new Promise(setImmediate); wrapper = wrapper.update().find("Test"); console.log(wrapper.debug()); });
日志输出
console.log testing at testing (tests/jest/specs/functional/TemplateCustomizationContent.js:18:13) console.log test has changed init at tests/jest/specs/functional/TemplateCustomizationContent.js:31:13 console.log <Test> <div> init </div> </Test> at Object.<anonymous> (tests/jest/specs/functional/TemplateCustomizationContent.js:132:13) console.log testing 2 at tests/jest/specs/functional/TemplateCustomizationContent.js:21:17 console.log <Test> <div> 1 </div> </Test> at Object.<anonymous> (tests/jest/specs/functional/TemplateCustomizationContent.js:135:13)
疑问:为什么Promise回调中的setTest执行后,监听test的useEffect没有输出"test has changed"日志?
解答
问题核心是测试代码没有等待React完成状态更新后的副作用执行流程。
当setTest(1)在Promise回调中被调用时,React会触发组件重渲染,随后执行依赖test的useEffect,但这个过程是异步的。你当前测试里只await了一次new Promise(setImmediate),这仅处理了第一轮宏任务,此时setTest触发的状态更新虽已完成渲染(从debug输出能看到值变成1),但对应的useEffect还没来得及执行,所以日志没打印出来。
解决办法:
- 方法1:使用
waitFor等待副作用执行
导入React Testing Library的waitFor,等待状态更新后的副作用完成:import { waitFor } from '@testing-library/react'; test.only("a", async () => { let wrapper = mount(<Test />); console.log(wrapper.debug()); await waitFor(() => { expect(wrapper.find('div').text()).toBe('1'); }); wrapper = wrapper.update().find("Test"); console.log(wrapper.debug()); }); - 方法2:多等待一轮宏任务
在测试中再加一次await new Promise(setImmediate),给React足够时间处理状态更新后的副作用:test.only("a", async () => { let wrapper = mount(<Test />); console.log(wrapper.debug()); await new Promise(setImmediate); await new Promise(setImmediate); // 新增这一行 wrapper = wrapper.update().find("Test"); console.log(wrapper.debug()); });
另外,测试React组件时,建议用act()包裹所有触发状态更新的操作,确保React的更新流程被正确处理,异步场景下这点尤为重要。
内容的提问来源于stack exchange,提问作者papillon
相关产品推荐
相关产品推荐

