Use cases for
useImperativeHandle are few and far between, but if you're testing a component that uses this React hook, you need to wrap any function you expose which results in state updates when called in React's
act utility. Otherwise you could be missing important things in your tests that could result in bugs shipped to production.
In this lesson we'll learn how to identify what needs to be wrapped in
act(...) and how to wrap those interactions.
This is based on my blog post: Fix the "not wrapped in act(...)" warning
Kent Dodds: [0:00] Here we have a function component that is using fowardRef so it can use that ref with a useImperativeHandle hook. We're attaching to the ref this function increment and decrement so that we can expose to consumers of our ImperativeCounter, so imperative methods that they can call to update the internal state of this counter component.
[0:20] To test this, we're creating a counterRef using React.createRef. We're rendering the ImperativeCounter with that counterRef, and then we verify that the screen has the count is zero rendered into the document. Then we call increment on the current counterRef. We verify that it says the count is one now.
[0:40] Then we decrement and verify that it says the count is zero. With that we have a passing test, but we're getting this warning that an update to fowardRef ImperativeCounter inside a test was not wrapped in act. We have some code that causes React state updates, but that wasn't wrapped in act properly.
[0:58] Without wrapping it in act, we can't be sure that we're testing behavior the user would actually see in the browser which is really important. With that in mind, we have to consider what interactions are we making that are triggering state updates.
[1:12] If we look at our component, there are two places that trigger state updates, the increment and decrement methods of our useImperativeHandle hook. Any time we call those two methods, we need to make sure it's wrapped in act.
[1:24] Luckily for us, we can grab act from the testing library React module, which is simply calling the ReactDOMTestUtils act function. Then we can ensure that we're interacting with the component the same way the user would. We'll wrap this in act and we'll wrap this in act.
[1:43] We save that, our test continues to pass and we're no longer getting that warning. In review, anytime you're testing a component that has a useImperativeHandle and you're calling functions on that imperative handle that results in state updates, you need to wrap those in act manually.