React setState in an axios call after switching pages quickly

Viewed 341

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

2 Answers

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.

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>

Related