material-components-web-react
material-components-web-react copied to clipboard
Can't perform a React state update on an unmounted component
Seeing this error when I hide a div that contains many ChipSet components.
index.js:1452 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 RippledComponent
in div
in ChipSet
It seems to happen right before execution of this line:
https://github.com/material-components/material-components-web-react/blob/a468f024df300c535d220f9bbc493e5c67af77d5/packages/chips/Chip.js#L74
I see there is no "mounted" check prior to this setState
call, maybe this could be the source of error.
@cdock1029 ah yes, we should have an isMounted check before both the remove and addClass methods (similar to https://github.com/material-components/material-components-web-react/blob/master/packages/ripple/index.js#L34). The foundation is calling these methods, but its most likely after they have been destroyed.
If you'd like, please feel free to open a PR following the pattern in the above link. Otherwise we will get to it asap.
Fix it with this technique.
Look where I added _isMounted
and follow the same in you code
class Page extends Component {
_isMounted = false;
state = {
isLoading: true
}
componentDidMount() {
this._isMounted = true;
callAPI_or_DB(...).then(result => {
if (this._isMounted) {
this.setState({isLoading: false})
}
});
}
componentWillUnmount() {
this._isMounted = false;
}
render() {
return (
<div>Whatever</div>
);
}
}
export default Page;
Thanks @mohokh67! For a more permanent solution, the Chip component should have built in internally.
Try https://github.com/iShawnWang/withUnmounted package as an elegant solution
尝试使用https://github.com/iShawnWang/withUnmounted包作为优雅的解决方案
npm ERR! 404 Not Found: @iShawnWang/withUnmounted@latest
Sorry for the case-sensitive : npm install --save @ishawnwang/withunmounted
works fine ~
I get similar warning in tests
<JssProvider generateClassName={generateClassName}>
<SnackbarProvider>
<StaticRouter location="/" context={{}}>
<Provider store={mStore}>
<Route path="/" component={Dashboard} exact />
</Provider>
</StaticRouter>
</SnackbarProvider>
</JssProvider>,
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 SnackbarProvider (at Dashboard.test.jsx:41) in JssProvider (created by WrapperComponent) in WrapperComponent
@mohokh67 What about solution for a functional component?
look at it: https://www.robinwieruch.de/react-warning-cant-call-setstate-on-an-unmounted-component
For functional components you can use a custom hook:
import { useEffect, useRef } from "react";
const useIsMounted = () => {
const isMounted = useRef(false);
useEffect(() => {
isMounted.current = true;
return () => (isMounted.current = false);
}, []);
return isMounted;
};
and then use it in your component:
const MyComp = () => {
const isMounted = useIsMounted();
useEffect(() => {
if (isMounted.current) {
// update your state here
}
}, [isMounted]);
/// ...
};
PS: This might not be worth while.
I have met this problem in a situation where the keys were massed up. So it might be coming from above the component itself. In my case the mistake was caused by how I have been generating the keys:
<ul>
{collection.map(o => <li key={JSON.stringify(o)}> {o.title} </li>)}
</ul>
That causes a memory leak as React can't notice some elements must be removed or created as the key changes on any other change in the collection element. My conclusion is that key should be unique during the life cycle so the state mechanism can rely on it.
how to resolve can't perform react state while using firestore
Try https://github.com/iShawnWang/withUnmounted package as an elegant solution
thanks @iShawnWang , work fine for me
i had to rerender the whole app in a specific case, so had changed key in useEffect of the container component. And this issue was coming.
what can I do I get the same problem that is can't perform a react state on an unmounted component...
`
const Header = () => { const [collection, setCollection] = useState([]);
const FetchCollection = async () => { const response = await fetch('http://localhost:5000/collection'); setCollection(await response.json([])); } console.log(collection)
useEffect(() => { FetchCollection(); }, []); `
what can I do I get the same problem that is can't perform a react state on an unmounted component...
`
const Header = () => { const [collection, setCollection] = useState([]);
const FetchCollection = async () => { const response = await fetch('http://localhost:5000/collection'); setCollection(await response.json([])); } console.log(collection)
useEffect(() => { FetchCollection(); }, []); `
In your case if the component is unmounted before the fetch return the setCollection will still be called. Also you don't return anything out of Header and I am not sure if that is intended.
One could use a ref to understand if the component was unmounted:
const Header = () => {
const ref = useRef(null);
const [collection, setCollection] = useState([]);
const fetchCollection = async () => {
const response = await fetch('http://localhost:5000/collection');
ref.current && setCollection(await response.json([]));
}
useEffect(() => {
ref.current = true
fetchCollection()
return () => {
ref.current = null
}
})
return <header>
{collection.map(item => <div key={JSON.stringify(item)}></div>)}
</hrader>
}