Reputation: 1279
Think of a component that has a prop 'name' and state 'elapse'.
new Component(name) => "Hi {name}. It's been {elapse} seconds"
{elapse}
should be reset to 0 when the prop {name}
changes.
If the prop changes from 'Alice' to 'Bob' at 10 seconds, the message should change from
Hi Alice. It's been 10 seconds
to
Hi Bob. It's been 0 seconds
getDerivedStateFromProps
cannot be used because {elapse}
is not a pure function of {name}
, and I cannot return 0 because it may be called on re-render.
componentDidUpdate
will eventually update {elapse}
to 0, but before that, the invalid state "Hi Bob. It's been 0 seconds" is shown to the user.
Can getDerivedStateFromProps
or componentDidUpdate
implement this scenario?
In many cases the state is not a pure function of props. Is getDerivedStateFromProps
only for stateless functional components? Does react encourage the use of stateless components?
How can getDerivedStateFromProps
replace componentWillReceiveProps
in stateful components?
Upvotes: 10
Views: 19369
Reputation: 811
I know there's already an accepted answer here and for some cases the mirroring of the props to the state may be required, but in your situation, I think that you actually want to make your component an 'uncontrolled component with a key' as explained here: https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#recommendation-fully-uncontrolled-component-with-a-key
Then you would use it like this:
<Component key={name} name={name}/>
And every time the name updated it would render a new instance of the Component, so would restart the counter.
Upvotes: 0
Reputation: 4387
If you look around you'll see you're not the first one having this problem - it was thoroughly discussed on github 1 2 3 and in a thread on hackernews here. The recommended solution is to mirror the props you need to check in the state:
state = { name: "", lastTime: Date.now() }
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.name !== prevState.name) {
return { name: nextProps.name, lastTime: Date.now() };
}
return null;
}
Upvotes: 15
Reputation: 3411
set initial state keep in mind the prop we pass down is { name:'alice' }
. But in order for getDerivedStateFromProps
to be able to see if name changed we must mirror it in our state and acess it with prevState.name
. We cannot use this
inside the lifecycle method because it is getDerivedStateFromProps
is pure
.
state = {time: 0, endtime: 0, name:'' }
When component mounts set the clock to begin incrementing by 1 every second.
componentDidMount() {
this.setClock();
}
When component unmounts clearInterval
componentWillUnmount() {
clearInterval(this.clockCall)
}
setInterval
will call incrementClock
every second increasing the timer by 1 every 1000 milliseconds (1 second).
setClock = () => {
this.setState({time: 0})
this.clockCall = setInterval(() => {
this.incrementClock();
}, 1000)
}
This is where we set the state of time and increment it every second.
incrementClock = () => {
this.setState((prevstate) => {time: prevstate.time + 1 })
}
If the name changes, we save the time in a new state piece called endtime
and that is what we use to display hey alice its been ${this.state.endtime} seconds
we also reset the timer to 0.
static getDerivedStateFromProps(nextProps, prevState) {
if(prevState.name !== nextProps.name)
return { time: 0, endtime:prevState.time };
return null;
}
Here are the two examples of how you would use getDerivedStateFromProps
compared to componentWillRecieveProps
Before
class ExampleComponent extends React.Component {
state = {
derivedData: computeDerivedState(this.props)
};
componentWillReceiveProps(nextProps) {
if (this.props.someValue !== nextProps.someValue) {
this.setState({
derivedData: computeDerivedState(nextProps)
});
}
}
}
After
class ExampleComponent extends React.Component {
// Initialize state in constructor,
// Or with a property initializer.
state = {};
static getDerivedStateFromProps(nextProps, prevState) {
if (prevState.someMirroredValue !== nextProps.someValue) {
return {
derivedData: computeDerivedState(nextProps),
someMirroredValue: nextProps.someValue
};
}
// Return null to indicate no change to state.
return null;
}
}
Upvotes: 2
Reputation: 1267
As of version 16.3, the recommended way to update state in response to props changes is with the new static getDerivedStateFromProps lifecycle. (That lifecycle is called when a component is created and each time it receives new props): See this: [link]https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html
Upvotes: -4