You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 19:14:57