React: getDerivedStateFromProps - responding to change in props

Created on 29 Aug 2018  Â·  7Comments  Â·  Source: facebook/react

The documentation states

This method exists for rare use cases where the state depends on changes in props over time

However, having read through https://github.com/facebook/react/issues/12188, it appears the only way to achieve this (checking props against prevProps to respond to a change) is by copying props into state - which appears to directly contradict your article on derived state anti patterns.

To say the least I'm a bit confused - should the documentation be changed (and getDerivedStateFromProps should not be used for responding to changes in props), or should I go ahead with the anti-pattern approach of copying props to state (pls no)? Or is there something I am missing?

TIA

Most helpful comment

Thanks for providing an example! This ensures the conversation isn't going in circles.

What I need is a way of setting followRegion back to true when the region passed down as props changes (this indicates new dataset)

As written in the blog post, the preferred alternative is to reset the state with a key.

<TripMap region={region} key={region.id} />

(If you don't have an ID property, you can just increment an integer every time you change the region or something like this.)

The mechanism you're suggesting ("automatically reset followRegion when region changes") is discouraged because the notion of "change" often doesn't really make sense here. For example, you might want to update region for some unrelated reason later without triggering a state reset — but now you don't have that option because components below assume every object identity change is a reset.

If you don't want to use the recommended key solution then sure, you'd need to keep prevRegion (or, better, prevRegionID) in the state. It doesn't contradict the article — this solution is explicitly mentioned as less recommended, but plausible. It's more verbose and usually unnecessary, but you can do it if you'd like.

Hope this helps.

All 7 comments

The lifecycle getDerivedStateFromProps should only be used if you absolutely 100% need to copy some props into state. That's the only use case for it. If you just need to react on prop changes you should move your code into the render method of your component.

The lifecycle getDerivedStateFromProps should only be used if you absolutely 100% need to copy some props into state

I don't agree with that at all.. I mean, it's in the name "derived state from props" i.e I want to _derive_ some state from my props...

If you just need to react on prop changes you should move your code into the render method of your component.

I need to react to a _change_ in prop, not just render differently based on props.

Let me give you a (simplified) example of my use case:

class TripMap extends React.Component {
  state = {
    followRegion: true,
  }

  render () {
    return [
      <Map
        region={followRegion ? region : null}
        // If the user tries to drag the map, give them control
        onMoveMap={() => this.setState({ followRegion: false })}
      />,
      <Button
        // If the user clicks, take back control of the region
        onClick={() => this.setState({ followRegion: true })}
      />Focus Region</Button>,
    ]
  }
}

What I need is a way of setting followRegion back to true when the region passed down as props changes (this indicates new dataset). This can be achieved with componentDidUpdate but this causes an unnecessary re-render (and triggers the eslint react/no-did-update-set-state rule from react/recommended, which is there for the exact reason afforementioned). I believe this is a totally valid (attempted) use-case of getDerivedStateFromProps

Thanks for providing an example! This ensures the conversation isn't going in circles.

What I need is a way of setting followRegion back to true when the region passed down as props changes (this indicates new dataset)

As written in the blog post, the preferred alternative is to reset the state with a key.

<TripMap region={region} key={region.id} />

(If you don't have an ID property, you can just increment an integer every time you change the region or something like this.)

The mechanism you're suggesting ("automatically reset followRegion when region changes") is discouraged because the notion of "change" often doesn't really make sense here. For example, you might want to update region for some unrelated reason later without triggering a state reset — but now you don't have that option because components below assume every object identity change is a reset.

If you don't want to use the recommended key solution then sure, you'd need to keep prevRegion (or, better, prevRegionID) in the state. It doesn't contradict the article — this solution is explicitly mentioned as less recommended, but plausible. It's more verbose and usually unnecessary, but you can do it if you'd like.

Hope this helps.

I can't go with key as this will cause a full re-render, which is very expensive (and the map component has to load async which displays a loader). I will go with storing it in state.
Thanks :beers:

It may also be worth mentioning this is causing eslint to complain about prevRegion being an unused state field - because it is only referenced in getDerivedStateFromProps via the state param that is passed, rather than via this (ergo eslint can't tell that I am referencing that state field).

Although, that is an eslint issue, and is already reported here.

That sounds like something that should be fixed in that ESLint rule.

Was this page helpful?
0 / 5 - 0 ratings