Suppose I am in Component A. In component A, I made an axios/api call request. While axios call is processing I moved to component B and then quickly came back to Component A(where axios call was being executed). When I receive the response and try to set state the console shows the error "Can not set state on unmounted component", even though the component is mounted. I did try the mount and unmount check but it is always showing me component is mounted. I tried everything but nothing worked. Help needed as I am new in React
Asked
Active
Viewed 453 times
2
-
1Does this answer your question? [useEffect - Can't perform a React state update on an unmounted component](https://stackoverflow.com/questions/59524063/useeffect-cant-perform-a-react-state-update-on-an-unmounted-component) – Vivek Doshi Jun 09 '20 at 15:16
-
@Vivek Doshi In the above link we can check the component is mounted. But in my case component is re mounted. Flow-> Mounted->Axios call->Unmount->Mount->Response received. In my case while receiving response the component is mounted but remember as axios call for fired in the old call stack of Component and this is the new call stack component. Therefore, old call stacks reponse is received in new call stack. Got it?.... – Mohit Gaur Jun 09 '20 at 15:25
2 Answers
1
you should make the api call in a parent component which will not unmount while waiting for the response. Then you can store the result in the parent component state and pass the value as a prop to Component A. Component A then becomes a stateless functional component which renders itself based on the value of the prop.

Ethan Lipkind
- 1,136
- 5
- 7
-
-
you'll have to structure your components such that the parent doesn't unmount when switching the page. This article may be helpful: https://reactjs.org/docs/lifting-state-up.html – Ethan Lipkind Jun 09 '20 at 15:29
-
But it will work if there's a relation between switching components. What if I totally move to some other page whose component is not related? then surely component will unmount – Mohit Gaur Jun 09 '20 at 15:34
-
1it depends on how you structure your application. if the user moves away and the requested data is no longer needed, then you can cancel the request and resend it once the user navigates back to the relevant page. but if you want to hold on to the data that is fetched regardless of whether the user navigates away, you should send the request and manage that state in a parent component that will always be mounted to the dom. alternatively, you could use something like a redux store to manage your global application state. – Ethan Lipkind Jun 09 '20 at 16:25
0
You can check if component is mounted inside useEffect
hook and additionally you can cancel requests in axios
For class component you'd put similar logic in componentDidMount()
componentWillUnmount()
Example
const { useState, useEffect, Component } = React;
const { Switch, Link, Route, HashRouter, useLocation, useHistory } = ReactRouterDOM;
class Component1Class extends Component {
constructor(props) {
super(props);
this.isUnmounted = false;
this.cancelToken = axios.CancelToken.source();
this.state = {
url: ''
}
}
componentDidMount() {
axios.get("https://i.picsum.photos/id/439/200/300.jpg", { cancelToken: this.cancelToken.token })
.then(response => {
if(this.isUnmounted) {
return;
}
this.setState({url: response.request.responseURL});
}).catch((err) => {
if (axios.isCancel(err)) {
console.log('Previous request canceled, new request is in proccess', err.message);
} else {
}
});
}
componentWillUnmount() {
this.isUnmounted = true;
this.cancelToken.cancel();
}
render() {
return <div>
<img src={this.state.url} alt="image"/>
</div>
}
}
const Component1 = () => {
const [url, setUrl] = useState('');
useEffect(() => {
let isUnmounted = false;
let cancelToken = axios.CancelToken.source();
axios.get("https://i.picsum.photos/id/439/200/300.jpg", { cancelToken: cancelToken.token })
.then(response => {
if(isUnmounted) {
return;
}
setUrl(response.request.responseURL);
}).catch((err) => {
if (axios.isCancel(err)) {
console.log('Previous request canceled, new request is in proccess', err.message);
} else {
}
});
return () => {
isUnmounted = true;
cancelToken.cancel();
}
}, [])
return <div>
<img src={url} alt="image"/>
</div>
}
const Component2 = () => {
return <div></div>
}
const App = () => {
return <div>
<div>
<Link to="/1">Component1</Link>
</div>
<div>
<Link to="/2">Component2</Link>
</div>
<Switch>
<Route path="/1" component={Component1Class}/>
<Route path="/2" component={Component2}/>
<Route component={Component1Class}/>
</Switch>
</div>
}
ReactDOM.render(
<HashRouter>
<App />
</HashRouter>,
document.getElementById('root')
);
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script src="https://unpkg.com/browse/react-router@3.0.2/umd/react-router.js"></script>
<script src="https://unpkg.com/react-router-dom@5.2.0/umd/react-router-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.js"></script>
<div id="root"></div>

Józef Podlecki
- 10,453
- 5
- 24
- 50
-
Well, in my case I have not implemented it through hooks. Any other approch? – Mohit Gaur Jun 09 '20 at 15:29
-
-
In my case component is already mounted. It is showing isMounted as true. The fact the axios call was fired in old instance of component and response is received in new instance of component. – Mohit Gaur Jun 09 '20 at 15:39
-
-
-
You should put api call and save it in parent component so you can reuse it. Your case is very weird its like you are saving the logic which happens inside `then` function in the example above and reuse it. That would explain the error – Józef Podlecki Jun 09 '20 at 16:09