Max Kurtz
Max Kurtz

Reputation: 478

this.props got updated before shouldComponentUpdate run

I have my ClockBlock component where I lifted state keeped in object "timer":

class ClockBlock extends Component {
  constructor(props){
    super(props);
    this.state = { timer: props.timer }; // timer from Redux store
  }

  render(){
    return(
      <div className="clockBlock">
        <Circle timer={this.state.timer} updateTimer={this.updateTimer.bind(this)} />
        <Clock timer={this.state.timer} updateTimer={this.updateTimer.bind(this)} />
        <ClockTerms updateTimer={this.updateTimer.bind(this)} />
      </div>
    )
  }

All the three nested component influence each other via updateTimer function. (except ClockTerms component - it works in one direction). The function is here:

 updateTimer(newDuration){
    const newState = Object.assign( {}, this.state );
    newState.timer.duration = newDuration;
    this.setState( newState );
  }

So, the problem - when I change timer using ClockTerms component I see changes in the Clock component too (via props, apparently). In the same time in the Circle component in shouldComponentUpdate function i'm trying to see the difference between the old props and the new. Here is this function:

 shouldComponentUpdate(nextProps, nextState){
    console.log( this.state.timer );
    console.log( nextState.timer );
    console.log( this.props.timer );
    console.log( nextProps.timer );

    return true;
 }

All the console.log() calls print the same data - new props. Why? And how can I get old props?

PS: i simplified my code above, removing irrelevant from my point of view calculations. Can give whole code, if it is important. I also use Redux here, but it seems to me it isn't engaged here much. Great thanks in advance!

UPDATE: I also get the same picture when place the same shouldComponentUpdate function in ClockBlock (parent) component;

Upvotes: 0

Views: 348

Answers (3)

Lex
Lex

Reputation: 5014

You can functionally set state. Because setState is async, when multiple things call setState React uses the most recent properties passed to setState.

Instead you can update state by passing an updater function. This batches all the state updates. When Reacts lifecycle begins to update state it'll process all the pending updater functions in sequence.

This is from the docs describing what happens when setState uses an object.

Subsequent calls will override values from previous calls in the same cycle, so the quantity will only be incremented once. If the next state depends on the current state, we recommend using the updater function form, instead:

this.setState((state) => {
  return {quantity: state.quantity + 1};
});

https://reactjs.org/docs/react-component.html#setstate

Upvotes: 1

Kodola Andrii
Kodola Andrii

Reputation: 11

I've solved a similar situation by comparing this.state to nextProps and updating state. I don't think it's a great solution, but didn't come up with anything else jet.

state = {
    dayOff: this.props.myProperty
}

shouldComponentUpdate(nextProps, nextState) {
    const shouldUpdate = this.state.myProperty !== nextProps.myProperty;
    if (shouldUpdate) this.state.myProperty = nextProps.myProperty
    return shouldUpdate;
}

Upvotes: 1

Max Kurtz
Max Kurtz

Reputation: 478

The point is that updateTimer fires BEFORE shouldComponentUpdate runs. That's it. So, when you update your lifted state in the parent component from children components (via passed in props function), keep in mind that shouldComponentUpdate will get already changed state and props.

Upvotes: 0

Related Questions