【问题标题】:Why do I need to wrap React/Enzyme state changes in act?为什么我需要将 React/Enzyme 状态更改包装在 act 中?
【发布时间】:2020-01-27 08:16:00
【问题描述】:

在我的简单 React/ReactDOM/Enzyme 单元测试中,我收到了来自 ReactDOM 的警告,关于将任何突变包装到 act() 中的状态。如果我的测试仍然通过,为什么我需要这样做?我有 50 个左右的 React 组件,它们都使用钩子、自定义钩子等,我从不包裹 act(),它们都通过了。

我可以禁用此警告吗?我不想添加似乎没有理由的额外代码。

警告:

Warning: An update to MyComponent inside a test was not wrapped in act(...).

    When testing, code that causes React state updates should be wrapped into act(...):

    act(() => {
      /* fire events that update state */
    });
    /* assert on the output */

    This ensures that you're testing the behavior the user would see in the browser. Learn more at [redacted]

我的测试:

const App = () => {
  const [isLoaded, setIsLoaded] = useState(false);

  const myOnClick = () => {
    setIsLoaded(true);
  };

  return (
    <div onClick={myOnClick}>
      {isLoaded ? 'Yes' : 'No'}
    </div>
  )
}
describe('My test', () => {
  let wrapper

  beforeAll(() => {
    wrapper = mount(<App />)
  })

  it('renders "no"', () => {
    expect(wrapper.text()).toBe('No')
  })

  describe('When onClick is called', () => {
    beforeAll(() => {
      wrapper.find('div').prop('onClick')()
    })

    it('renders "yes"', () => {
      expect(wrapper.text()).toBe('Yes')
    })
  })
})

CodeSandbox 重现:https://codesandbox.io/s/react-169-act-warning-repro-olm8o?expanddevtools=1&fontsize=14&hidenavigation=1&previewwindow=tests

【问题讨论】:

    标签: reactjs jestjs enzyme react-dom


    【解决方案1】:

    导致act() 警告的行是

    wrapper.find('div').prop('onClick')()
    

    正如@skyboyer 的回答所建议的那样,prop 内部没有用act 包裹。当您调用 onClick 并更新状态时,react 会警告您发生意外更改。如果您改用.simulate,则不需要用act 包装它。

    wrapper.find('div').simulate('click')
    

    【讨论】:

      【解决方案2】:

      act() 首先运行所有相关的useEffect,否则将是异步方式。您看不到任何区别,因为您的组件代码中没有 useEffect

      根据最新版本中的Enzyme's docs mount() 应该已经在内部用act() 包裹:

      如果您使用 React 16.8+ 和 .mount(),Enzyme 将包装 API,包括 .simulate().setProps().setContext().invoke()ReactTestUtils.act(),因此您无需手动包装它.

      我们不能在 Enzyme 内部用 .act() 包装 .prop()(或 .props())的结果,因为它会破坏返回值的相等性。但是,您可以使用.invoke() 来简化代码:

      const wrapper = mount(<SomeComponent />);
      wrapper.invoke('handler')();
      expect(/* ... */);```
      

      【讨论】:

      • 感谢您的回答。我的组件没有使用 useEffect 但 ReactDOM 仍然会抱怨,所以我假设使用 useState 也有同样的问题。
      • 无论是否存在 useEffect 钩子,它都会抱怨。在单元测试中不要假设此类实现细节也是一个好习惯,因为将来代码可能会发生变化。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-01
      • 2016-11-29
      • 1970-01-01
      相关资源
      最近更新 更多