Reputation: 478
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
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
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
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