Reputation: 340
The app I'm working on displays a user dashboard on login with a sidebar for navigation. It uses firebase. I do most of my data fetch from firebase in my async componentDidMount()
and store the data in my component state. It takes a couple of seconds to finish all fetches. But if the user decides to navigate to another screen before the fetch is complete, I get the
Can't call setState on unmounted component
warning (as expected). So I do some digging and find that
if(this.isMounted()) this.setState({updates:updates})
makes the warning go away, but then I also find that using isMounted
is an antipattern.
The official documentation on this issue suggests tracking the mounted state ourselves by setting _isMounted=true
in componentDidMount
and then set it to false in the componentWillUnmount
. The only way I see to achieve this would be through a variable in component state. Turns out, setState doesn't work in componentWillUnmount. [Issue 1] (I tried calling an external function from componentWillUnmount
which in turn sets the state variable. Didn't work.)
The documentation suggests another way, to use cancellable promises. But I'm clueless about how to achieve that with await firebase
calls. I also couldn't find any way to stop firebase calls mid-track. [Issue 2]
So now I'm stuck with the warning and data leaks.
a. How do I resolve this problem?
b. Is this something I need to take seriously?
Upvotes: 4
Views: 1305
Reputation: 112777
It's good practice to check if the component is still mounted when a request completes, if there is a risk of the component unmounting.
You don't need to put _isMounted
in your component state since it will not be used for rendering. You can put it directly on the component instance instead.
Example
class MyComponent extends React.Component {
state = { data: [] };
componentDidMount() {
this._isMounted = true;
fetch("/example")
.then(res => res.json())
.then(res => {
if (this._isMounted) {
this.setState({ data: res.data });
}
});
}
componentWillUnmount() {
this._isMounted = false;
}
render() {
// ...
}
}
Upvotes: 3