5

I have React lifecycle method as follows:

componentWillReceiveProps(nextProps){
    if(this.props.totalVehicles !== nextProps.totalVehicles){
        this.setState({animation: "cartCount"}, () => setTimeout(() =>  this.setState({animation: null}), 1000));
    }
}

But that gives me:

Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the Header component.

How to set state in the lifecycle methods without getting those errors?

Boky
  • 11,554
  • 28
  • 93
  • 163
  • Ideally, you can set state in `componentWillReceiveProps`, the error could be because you are using `setTimeout`. It is setting state after the mounting is done, thus the error. – Umesh Jan 22 '18 at 09:39
  • Are you somehow navigating away from the component before setTimeout is executed, you might try to use this solution https://stackoverflow.com/questions/39767482/is-there-a-way-to-check-if-the-react-component-is-unmounted/39767963#39767963 and setState in setTimeout only if the component is mounted – Shubham Khatri Jan 22 '18 at 11:03
  • @ShubhamKhatri Nope. – Boky Jan 22 '18 at 11:03
  • Your code seems correct otherwise, I tried to make a demo of it and its works fine https://codesandbox.io/s/ymmp7vxw61 – Shubham Khatri Jan 22 '18 at 11:04

4 Answers4

2

Although this is an old questions, i'll answer it for future references.

When using setState (which is asynchronous) via setTimeout, you have to remember to clear the timeout on componentWillUnmount. Otherwise, you might get to situations in which the setState is called after the element has already unmounted.

Ofir
  • 1,565
  • 3
  • 23
  • 41
1

How about setting it on componentWillUpdate? That way, you know that the component has mounted already. Docs here

If you want to set up initial state, do it in componentWillMount.

More lifecycle methods in here

jamcreencia
  • 6,474
  • 1
  • 11
  • 7
0

You need to check if you component is mounted now. You can create trigger mounted in your state and manage it in lifecycles methods.

constructor(props) {
 super(props);
 this.state = { mounted: false };
}

componentDidMount() {
 this.setState({ mounted: true });
}

componentWillUnmount() {
 this.setState({ mounted: false });
}

componentWillReceiveProps(nextProps){
 if(this.props.totalVehicles !== nextProps.totalVehicles && 
  this.state.mounted){
   this.setState({animation: "cartCount"}, () => setTimeout(() =>
    this.setState({animation: null}), 1000));
 }
}
Artem Mirchenko
  • 2,140
  • 1
  • 9
  • 21
0

Instead of using state "mounted" try using instance variable "mounted":

constructor(props) {
 super(props);
 this.mounted = false;
}

componentDidMount() {
 this.mounted = true;
}

componentWillUnmount() {
 this.mounted = false;
}

componentWillReceiveProps(nextProps){
 if(this.props.totalVehicles !== nextProps.totalVehicles && 
  this.mounted){
   this.setState({animation: "cartCount"}, () => setTimeout(() =>
    this.setState({animation: null}), 1000));
 }
}
Piyush
  • 3,015
  • 3
  • 18
  • 31