React: When To Use act()

2023-03-04
React
testing
best practices
Jest

Long story short

In another article before (React: Why Not To Use act() (In Most Of The Cases)), I've outlined why the act() is probably not something one should use when faced with a "not wrapped in act(...)" warning in tests using React Testing Library (TL;DR most of the React Testing Library functions are already wrapped in act() internally).

But then, why does it exist and when to use it? There's a long and detailed article on the subject: https://kentcdodds.com/blog/fix-the-not-wrapped-in-act-warning#other-use-cases-for-manually-calling-act but in short, when the status update in the code executed asynchronously is not going to be executed within React's callstack - for instance,

  • When using jest.useFakeTimers() to manipulate the timers and timer-controlled functionality, so e.g. jest.advanceTimersByTime(1000) should be called as act(() => jest.advanceTimersByTime(1000)) (there's a detailed example in the aforementioned article).
  • When testing custom hooks: if a hook returns a function that could manage a state later, its call might need to be wrapped in act() as act(() => result.current.functionThatSetsState()) for the const {result} = renderHook(() => useHookThatReturnsStaetSetterFunction()).
  • When using useImperativeHandle hook - I'm not familiar with it, but to quote the article, "you only run into this if you're calling methods directly on a component which do internal state updates and you're outside of React's callstack".