163

It looks like componentWillReceiveProps is going to be completely phased out in coming releases, in favor of a new lifecycle method getDerivedStateFromProps:static getDerivedStateFromProps().

Upon inspection, it looks like you are now unable to make a direct comparison between this.props and nextProps, like you can in componentWillReceiveProps. Is there any way around this?

Also, it now returns an object. Am I correct to assume that the return value is essentially this.setState?

Below is an example I found online: State derived from props/state.

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;
  }
}
Edgar
  • 6,022
  • 8
  • 33
  • 66
Andrew
  • 7,201
  • 5
  • 25
  • 34
  • 2
    Title should be "how to make things work despite React team doing everything to oppose it" – zyrup Mar 17 '21 at 20:25

4 Answers4

117

About the removal of componentWillReceiveProps: you should be able to handle its uses with a combination of getDerivedStateFromProps and componentDidUpdate, see the React blog post for example migrations. And yes, the object returned by getDerivedStateFromProps updates the state similarly to an object passed to setState.

In case you really need the old value of a prop, you can always cache it in your state with something like this:

state = {
  cachedSomeProp: null
  // ... rest of initial state
};

static getDerivedStateFromProps(nextProps, prevState) {
  // do things with nextProps.someProp and prevState.cachedSomeProp
  return {
    cachedSomeProp: nextProps.someProp,
    // ... other derived state properties
  };
}

Anything that doesn't affect the state can be put in componentDidUpdate, and there's even a getSnapshotBeforeUpdate for very low-level stuff.

UPDATE: To get a feel for the new (and old) lifecycle methods, the react-lifecycle-visualizer package may be helpful.

Oblosys
  • 14,468
  • 3
  • 30
  • 38
  • 1
    Ugh, I messed up the question. I actually meant `componentWillReceiveProps` – Andrew Apr 02 '18 at 22:09
  • 2
    I had thought of using my state to hold previous props, but I really wanted to avoid the extra code and logic it takes to implement it. I'll looking into some of the other things you bring up. Much thanks! – Andrew Apr 02 '18 at 23:39
  • 4
    Having to store a previous prop in a state is just a boilerplate workaround for this hard-to-understand React API change. For the eyes of many developers this looks like an antipattern and a regression change. Not criticising you Oblosys, but the React team. – AxeEffect May 04 '18 at 09:11
  • 2
    @AxeEffect This is because **`getDerivedStateFromProps` was never really intended for memoization**. Please see my answer below where I described **the recommended approach** instead. – Dan Abramov Jun 10 '18 at 13:31
  • is that a typo? Did you miss `...`? That is should we return the entire state object or only the part that we care about. – theprogrammer Jun 27 '19 at 16:30
  • You should just return the derived state properties. I denoted possible other derived properties with `..` to avoid confusion with `...`, but `// ...` is better. – Oblosys Jun 27 '19 at 17:51
62

As we recently posted on the React blog, in the vast majority of cases you don't need getDerivedStateFromProps at all.

If you just want to compute some derived data, either:

  1. Do it right inside render
  2. Or, if re-calculating it is expensive, use a memoization helper like memoize-one.

Here's the simplest "after" example:

import memoize from "memoize-one";

class ExampleComponent extends React.Component {
  getDerivedData = memoize(computeDerivedState);

  render() {
    const derivedData = this.getDerivedData(this.props.someValue);
    // ...
  }
}

Check out this section of the blog post to learn more.

Dan Abramov
  • 264,556
  • 84
  • 409
  • 511
  • 54
    If it's not needed in _vast_ majority of cases, then I'm surprised this was such a badly needed change, the one that will break thousands of working projects. Looks like React team started over engineering. – Ska Jun 28 '18 at 10:49
  • 1
    What change are you referring to? What will break working projects? There was no breaking change. – Dan Abramov Jun 29 '18 at 15:42
  • 45
    Change from componentWillReceiveProps to getDerivedStateFromProps. It's not breaking but forcing to refactor all existing code, which is very time consuming. And seems to very little benefit since you say you shouldn't be using it at all in vast majority of cases. Why go through the hassle of changing API for something that shouldn't even be used in the first place. – Ska Jul 02 '18 at 06:20
  • 4
    I would love a response to this comment from Dan Abramov. – Louis345 Oct 20 '18 at 15:48
  • 6
    @DanAbramov any answer on why this change came to be? – Petros Kyriakou Jan 03 '19 at 14:07
  • 4
    Actually in our projects this is used a lot. For showing things like Snackbars on screens for when new data comes down, 1 example. ```componentWillReceiveProps``` was simple and it worked. Why remove it for this static garbage... – Oliver Dixon Jul 27 '19 at 09:08
  • 1
    @DanAbramov I'm curious too about an answer, just why? – alek kowalczyk Aug 13 '19 at 12:42
  • @DanAbramov I had use case where I directly (without any conditions) fetch data in componentwillReceive Props, and store result in state. what is best way to transition this to new react lifecycle methods? (don't easily see one) – Giorgi Moniava Aug 28 '19 at 19:39
6

As mentioned by Dan Abramov

Do it right inside render

We actually use that approach with memoise one for any kind of proxying props to state calculations.

Our code looks this way

// ./decorators/memoized.js  
import memoizeOne from 'memoize-one';

export function memoized(target, key, descriptor) {
  descriptor.value = memoizeOne(descriptor.value);
  return descriptor;
}

// ./components/exampleComponent.js
import React from 'react';
import { memoized } from 'src/decorators';

class ExampleComponent extends React.Component {
  buildValuesFromProps() {
    const {
      watchedProp1,
      watchedProp2,
      watchedProp3,
      watchedProp4,
      watchedProp5,
    } = this.props
    return {
      value1: buildValue1(watchedProp1, watchedProp2),
      value2: buildValue2(watchedProp1, watchedProp3, watchedProp5),
      value3: buildValue3(watchedProp3, watchedProp4, watchedProp5),
    }
  }

  @memoized
  buildValue1(watchedProp1, watchedProp2) {
    return ...;
  }

  @memoized
  buildValue2(watchedProp1, watchedProp3, watchedProp5) {
    return ...;
  }

  @memoized
  buildValue3(watchedProp3, watchedProp4, watchedProp5) {
    return ...;
  }

  render() {
    const {
      value1,
      value2,
      value3
    } = this.buildValuesFromProps();

    return (
      <div>
        <Component1 value={value1}>
        <Component2 value={value2}>
        <Component3 value={value3}>
      </div>
    );
  }
}

The benefits of it are that you don't need to code tons of comparison boilerplate inside getDerivedStateFromProps or componentWillReceiveProps and you can skip copy-paste initialization inside a constructor.

NOTE:

This approach is used only for proxying the props to state, in case you have some inner state logic it still needs to be handled in component lifecycles.

mpospelov
  • 1,510
  • 1
  • 15
  • 24
0

getDerivedStateFromProps is used whenever you want to update state before render and update with the condition of props

GetDerivedStateFromPropd updating the stats value with the help of props value

read https://www.w3schools.com/REACT/react_lifecycle.asp#:~:text=Lifecycle%20of%20Components,Mounting%2C%20Updating%2C%20and%20Unmounting.