I was running some tests on a react-router-dom Component Class, using React Testing Library and Jest.
The class makes a fetch call to my API and returns the list of posts for this blog. The list then has links to edit or deactivate the posts..
Running the page by itself, I receive no errors.
Warning
But when I run it through Jest, I receive a warning, even though all the tests passed.
console.error node_modules/@testing-library/react/dist/act-compat.js:52
Warning: Can't perform a React state update on an unmounted component.
This is a no-op, but it indicates a memory leak in your application.
To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
in Posts (at App.js:57)
in Route (at App.js:56)
in Switch (at App.js:53)
in Router (created by BrowserRouter)
in BrowserRouter (at App.js:43)
in App (at App.test.js:83)
in Router (at App.test.js:65)
Test Scenario
My test is simulating a click on a Posts link.
it('should navigate to the posts page resolved', async () => {
const { getByText , getByTestId } = renderWithRouter()
fireEvent.click(getByTestId('Posts'))
const posts = await waitForElement(() => getByText("Title"))
expect(posts).toHaveTextContent('Title')
})
Solution
I googled "cancel all subscriptions and asynchronous tasks in the componentwillunmount method" and came across this solution.
Based on the solution, I added an _isMounted variables to the class's this scope, to keep track of the fetch callback and to set when mounting and unmounting the component:
Constructor
this._isMounted = false;
Fetch
In my fetch result and error blocks:
getPosts() {
fetch(`${process.env.REACT_APP_API_URL}/getPosts`)
.then(res => res.json())
.then(
result => {
this._isMounted && this.setState({
isLoaded : true,
entries : result.posts,
activeEntries : result.posts.filter(entry => entry.deletedAt === null)
})
},
error => {
this._isMounted && this.setState({
isLoaded : false,
error
})
}
)
}
componentDidMount() / componentWillUnmount()
componentDidMount() {
this._isMounted = true;
this._isMounted && this.getPosts();
}
componentWillUnmount() {
this._isMounted = false;
}
Code commit here.
Comments