Do you want to request a feature or report a bug?
Not really either; perhaps just an issue with the act
design?
What is the current behavior?
I receive the Warning: An update to ... inside a test was not wrapped in act(...).
warning if a component under test has a useEffect
that makes a change to itself async (I assume because the component updates itself after the act
block)
If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem. Your bug will get fixed much faster if we can run your code and it doesn't have dependencies other than React. Paste the link to your JSFiddle (https://jsfiddle.net/Luktwrdm/) or CodeSandbox (https://codesandbox.io/s/new) example below:
I think I've distilled this as far as I can:
// This is the component under test
function TestComponent() {
const [initialized, setInitialized] = React.useState(false);
// this function would really be a jest mock.fn of an async http request.
// just put it here to simplify the test.
const loadData = () => {
return new Promise(resolve => {
resolve();
});
};
React.useEffect(() => {
initialize();
}, []);
async function initialize() {
await loadData();
setInitialized(true); // <-- this change seems to trigger the `act` warning, but I can't wrap it in an act...
}
return <span>{initialized ? 'loaded' : 'loading...'}</span>;
}
// And this is the test for the above component
beforeEach(() => {
container = document.createElement('div');
document.body.appendChild(container);
});
afterEach(() => {
document.body.removeChild(container);
container = null;
});
test('act works in this case', () => {
act(() => {
ReactDOM.render(<TestComponent />, container);
});
});
What is the expected behavior?
The render is happening within the act, but I'm assuming the setInitialized
is probably being fired async after the act
exits.
I'm not entirely sure how to handle this case, since I can't put an act
inside the component being tested. Any recommendations or guidance would be much appreciated.
Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
"react": "16.8.6",
"react-dom": "16.8.6",
"jest": "24.7.1",
This is fixed in master; we now have an async version of act()
. Here's the original issue, with examples on how async act can scope these updates correctly https://github.com/facebook/react/issues/14769#issuecomment-479942713 . It's available in 16.9.0-alpha, and will be included in our next release. In your specific case, with the new alpha, this would pass your tests.
test('act works in this case', async () => {
await act(async () => {
ReactDOM.render(<TestComponent />, container);
});
});
Closing this issue, but feel free to reach out and comment on the other one.
Awesome, thank you!
Just wanted to confirm that in v16.9.0-alpha the async act function does fix my issue. 👍
i'm having trouble with typescript however, is there any way to contribute or work around until release?
Is there any workaround to fix this until 16.9.0 is released
I updated my react version to 16.9.0-alpha.0 but I am still getting the same warning.
Do I need to update other dependencies? I included my dependency list here.
"dependencies": {
"@material-ui/core": "^4.2.0",
"@material-ui/icons": "^4.2.1",
"axios": "^0.19.0",
"client-oauth2": "^4.2.4",
"enzyme": "^3.10.0",
"enzyme-adapter-react-16": "^1.14.0",
"react": "^16.9.0-alpha.0",
"react-dom": "^16.9.0-alpha.0",
"react-redux": "^7.1.0",
"react-router-dom": "^5.0.1",
"react-scripts": "3.0.1",
"react-test-renderer": "^16.9.0-alpha.0",
"redux": "^4.0.1",
"redux-thunk": "^2.3.0"
}
@threepointone Thank's, it's work
For those that can't yet upgrade to 16.9: https://github.com/facebook/react/issues/14769#issuecomment-514589856
I'm using 16.9 with @testing-library/react
and still running into this!
Wrapping it in the above await...async... example makes things go away.
Same I'm using 16.9 with @testing-library/react and still running into this!
Yea I still get this after updating to 16.9
jest.mock('../../apis/getGroups', () => ({
getGroups: () =>
Promise.resolve({
groups: [
{ id: '1', name: 'group1' },
{ id: '2', name: 'group2' }
]
})
}));
let container;
describe('User component', () => {
beforeEach(() => {
container = document.createElement('div');
document.body.appendChild(container);
});
afterEach(() => {
unmountComponentAtNode(container);
container.remove();
container = null;
});
test('it shows a list of users', async () => {
await act(async () => {
render(<MyWidget groupId={'2'} />, container );
});
expect(container.textContent).toBe('What to expect?');
});
});
Having the same issue with 16.12.0
. The workaround with await act(async () => { ... })
does not get rid of the warning.
I was able to get rid of the warnings from @testing-library/react
this way:
import { render, act } from '@testing-library/react';
async function renderProvider(props) {
const tree = (
<SchemaProvider {...props}>
<SchemaContext.Consumer>
{value => <span data-testid="output">{JSON.stringify(value)}</span>}
</SchemaContext.Consumer>
</SchemaProvider>
)
// before: (gave warnings)
// return render(tree)
//
// fixed:
let provider
await act(() =>
provider = render(tree)
);
return provider
}
possibly a clue for the RTL folks? @kentcdodds
Hi @acao, the reason yours is working is because you added await
there which ensures that you wait until the next tick of the event loop before continuing. That "works" but it could stop working unexpectedly (if you add more async stuff on additional ticks of the event loop). There are better ways to solve this problem. Learn all about this from my blog (and never have a question about this warning again) 😉: https://kentcdodds.com/blog/fix-the-not-wrapped-in-act-warning
@kentcdodds Thanks for this, but OMG complex. I read your post and my head is still spinning... it makes me not want to write tests at all, which is obviously not a good thing.
In my own project, I ended up just silencing the warnings after a while. Maybe some day I'll spend the time to figure it out.
Well, from the post:
So the act warning from React is there to tell us that something happened to our component when we weren't expecting anything to happen.
So if you see the warning, it's because your test is incomplete. If you use React Testing Library utilities, you very rarely need to use act. So there's not much to this honestly 🤷♂️
Sure... but what? How about telling us what that 'something' is, so that it is easier to figure out? In my case, my library is wrapping another library components and who knows if the problem is in my component or deeper down the chain. Again, help remove the complication somehow.
I'm sure the react team would welcome any contributions you can make to improve the warning. My guess is they're already giving you all the information they have.
@kentcdodds Ok, you motivated me to try again and I was able to make all the tests pass without silencing the warnings. Thanks.
I'm still a bit stumped on this one...I'm a bit new to hooks. But say I have a component that looks like this (with a pinch of salt):
const EventsView: React.FC<Props> = ({ fetchEvents, eventsFromRedux }) => {
const [events, setEvents] = useState(null);
useEffect(() => {
// fetch some events on mount
fetchEvents([1, 2, 3]);
}, []);
useEffect(() => {
// When we get some eventsFromRedux transform the data
if (eventsFromRedux.length > 0) {
const transformedEvents = someTransformHelper(eventsFromRedux);
setEvents(someTransformHelper);
}
}, [eventsFromRedux]);
if (!events) {
return null;
}
return <div>My transformed events</div>;
};
It does a few things:
eventsFromRedux
so we can use them in the componentand in my tests I get the above warning:
Warning: An update to EventsView inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act
in this way? I see the wait
for the promise
in this example: https://kentcdodds.com/blog/write-fewer-longer-tests but not too sure how this would relate to the component above. useEffect
s to listen for the data change?I hoped I would write some tests like:
Note:
I transform the data within the component because I have multiple apps that share a "core" backend/redux. Kinda of.
This is fixed in master; we now have an async version of
act()
. Here's the original issue, with examples on how async act can scope these updates correctly #14769 (comment) . It's available in 16.9.0-alpha, and will be included in our next release. In your specific case, with the new alpha, this would pass your tests.test('act works in this case', async () => { await act(async () => { ReactDOM.render(<TestComponent />, container); }); });
Closing this issue, but feel free to reach out and comment on the other one.
How do you make assertions with this? @threepointone?
test('act works in this case', async () => {
const { getByText } = await act(async () => {
ReactDOM.render(<TestComponent />, container);
});
expect(getByText("Title")).toBe("test")
});
???
I could help you, but please make a minimal reproducing case on codesandbox, or as a git repo. Thanks!
Thanks @threepointone but I just wrapped everything in the act
like this:
await act(async () => {
const { getByText } = render(
<Provider store={store}>
<MyComponent />
</Provider>
);
expect(getByText("My component title"));
});
which seems to work.
still can't get it :(
I have code like this.
const [contacts, setContacts] = useState([]);
const fetchData = async () => {
const data = await fetch("http://localhost:3000/contacts")
.then((response) => response.json())
.catch((err) => {
console.log(err);
return false;
});
if (data) {
setContacts(data);
}
};
useEffect(() => {
!contacts.length && fetchData();
}, []);
and my test is
test("should fetch and render all contacts", async () => {
const {container} = render(<App />);
const users = await fetch(
"http://localhost:3000/contacts"
).then((response) => response.json());
const userElements = container.querySelectorAll("li");
expect(userElements.length).toEqual(users.length);
});
Not sure how should I handle it.
@kentcdodds I read your articles, but still not sure how to do that.
I refactored my useEffect, thanks for that. But still how to use act :)
My component looks now like that:
const [contacts, setContacts] = useState([]);
const fetchData = async () => {
return await fetch("http://localhost:3000/contacts")
.then((response) => response.json())
.catch((err) => {
console.log(err);
return false;
});
};
useEffect(() => {
(async () => {
if (!contacts.length) {
const data = await fetchData();
data && setContacts(data);
}
})();
}, []);
I was in similar situation, where I was testing a Form component, I mocked the api module and I am changing a loader state from true to false on response of the api, and In the DOM I was disabling a button until response comes from api, and I was getting this "not wrapped in act(...)" warning
solution from https://testing-library.com/docs/react-testing-library/faq works for me, by using findBy* query after submit button gets clicked.
This warning is usually caused by an async operation causing an update after the test has already finished. There are 2 approaches to resolve it:
Wait for the result of the operation in your test by using one of the async utilities like wait or a find* query. For example: const userAddress = await findByLabel(/address/i).
Mocking out the asynchronous operation so that it doesn't trigger state updates.
Generally speaking, approach 1 is preferred since it better matches the expectations of a user interacting with your app.
taken from https://testing-library.com/docs/react-testing-library/faq
Wow, awesome, thx!!!
Most helpful comment
This is fixed in master; we now have an async version of
act()
. Here's the original issue, with examples on how async act can scope these updates correctly https://github.com/facebook/react/issues/14769#issuecomment-479942713 . It's available in 16.9.0-alpha, and will be included in our next release. In your specific case, with the new alpha, this would pass your tests.Closing this issue, but feel free to reach out and comment on the other one.