react-testing-library: useEffect with async function call causes `act(...)` warning
When rendering and testing a component with a useEffect that calls an async function that modifies the component’s state, then an act() console error will be output when running tests.
@testing-library/reactversion: 10.0.4jestversion: 26.0.1- DOM Environment:
jsdomversion: 16.2.2
Relevant code or config:
const MyComponent = () => {
const [loaded, setLoaded] = useState(false)
useEffect(() => {
async function load() {
await loadFromApi()
setLoaded(true)
}
load()
}, [])
return loaded ? <div>loaded</div> : <div>loading...</div>
}
// timeout=0 to simulate mocked api responses
const loadFromApi = () => new Promise(resolve => setTimeout(resolve, 0))
What you did:
Just render this component in a jest test.
test('act() console error', () => {
const sut = render(<MyComponent />)
expect(sut).toBeDefined()
})
What happened:
This error is output in my project when running tests:
console.error
Warning: An update to null inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
Reproduction:
See console output: https://codesandbox.io/s/react-testing-library-demo-sk2so
Problem description:
Rendering should wait until everything has been resolved.
Suggested solution:
Since async functions inside useEffect are quite common, rendering a component containing such a hook should wait for the component updates to have finished (with a small timeout).
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 36
- Comments: 19 (7 by maintainers)
Commits related to this issue
- fix(bmd): cancel printer status request on unmount It's possible that the `readPrinterStatus` call will not resolve until after the component has unmounted, in which case the `setPrinterStatus` call ... — committed to votingworks/vxsuite by eventualbuddha 2 years ago
- fix(bmd): cancel printer status request on unmount It's possible that the `readPrinterStatus` call will not resolve until after the component has unmounted, in which case the `setPrinterStatus` call ... — committed to votingworks/vxsuite by eventualbuddha 2 years ago
Hi @fabb,
There’s no way for React Testing Library to know that you’ve got async stuff happening in the background, and you wouldn’t want that anyway because you probably want to assert the “loading” state anyway.
This is why React Testing Library gives you async utils which you can use to wait for the UI to update asynchronously.
Here’s how you’d fix that codesandbox: https://codesandbox.io/s/react-testing-library-demo-dmklb?file=/src/__tests__/hello.js:244-306
The cool thing about this is the async utils provided by React Testing Library are automatically wrapped in
actso you shouldn’t ever have to worry about usingactmanually. Learn more here: https://kcd.im/act-warningGood luck 😃
You could bring this question to my https://kcd.im/office-hours
Can’t answer here/now
@kevinvugts, could you bring this to https://testing-library.com/discord or https://kentcdodds.com/discord instead?
@queeniema, I don’t typically write jest tests that hit the real API and take a long time to run. I mock things like that so they’re fast and then test those actual calls with an E2E testing tool like Cypress.
If you have any follow-up questions, please take them to https://testing-library.com/discord
Yeah, I suggest opening a new issue with a repro. I have a feeling there’s not much we can do about this though. You’ll probably have to have an explicit unmount and wait for the cleanup to finish.
@queeniema The reason it’s failing is because
screen.findByText(/loaded/i)useswaitForand the default timeout is1000ms. You’ll need to change it to a higher number for thefindBy*query to wait longer. You can see the API here. I tried changing that in the codesandbox but it looks like it doesn’t change the default test timeout.Hi guys, I am having the following test wrapped in an async function:
This throws warning:
None of the advice here helped, any clue?
OMG I was stuck with this warning for 24 hours. Thank you for your help, this worked.
BTW thanks for the tip of updating jest to include a backtrace in the error log output, that helps a lot.