2

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

Mohit Gaur
  • 355
  • 1
  • 3
  • 22
  • 1
    Does 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 Answers2

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
  • But won't parent component get unmounted after switching page? – Mohit Gaur Jun 09 '20 at 15:27
  • 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
  • 1
    it 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