mq7
mq7

Reputation: 1279

Compare with previous props in getDerivedStateFromProps

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

Can getDerivedStateFromProps or componentDidUpdate implement this scenario?

  1. 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?

  2. How can getDerivedStateFromProps replace componentWillReceiveProps in stateful components?

Upvotes: 10

Views: 19369

Answers (4)

JSoet
JSoet

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

Al.G.
Al.G.

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

Omar
Omar

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

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

Related Questions