Amanshu Kataria

Software Engineer

Replacing ‘componentWillReceiveProps’ with ‘getDerivedStateFromProps’

With the release of React 16.3, some new lifecycle methods have been introduced, and release of React 17 will deprecate some lifecycle method.
getDerivedStateFromProps
is one of those newly introduced lifecycle method replacing
componentWillReceiveProps
, which has now become
UNSAFE_componentWillReceiveProps
.
getDerivedStateFromProps
is a static method which is invoked after a component is instantiated as well as when it receives new props. Since it is a static method, you cannot access
this
inside this method neither you can access any other class method. Unlike
componentWillReceiveProps
you cannot set state inside this method, so the only way to update state is returning an object. If you don’t want to update any state, simply return
null
.
Let’s dive into some code
This is how
componentWillReceiveProps
works.
componentWillReceiveProps(nextProps){
  if(nextProps.someValue!==this.props.someValue){
    //Perform some operation
    this.setState({someState: someValue });
    this.classMethod();
  }
}
We compare
nextProps.someValue
with
this.props.someValue
and if both are different then we perform some operation,
setState
and call
this.classMethod();
.
Now let’s have a look how
getDerivedStateFromProps
works.
static getDerivedStateFromProps(nextProps, prevState){
   if(nextProps.someValue!==prevState.someValue){
     return { someState: nextProps.someValue};
  }
  else return null;
}

componentDidUpdate(prevProps, prevState) {
  if(prevProps.someValue!==this.props.someValue){
    //Perform some operation here
    this.setState({someState: someValue});
    this.classMethod();
  }
}
It receives two params
nextProps
and
prevState
. As mentioned previously you cannot access
this
inside this method so you’ll have to store the props in the state to compare the
nextProps
with previous props. In above code
nextProps
and
prevState
are compared, if both are different then an object will be returned to update the state otherwise
null
will be returned indicating state update not required. If state changes then
componentDidUpdate
is called where we can perform the desired operations as we did in
componentWillReceiveProps
.
Let’s make it more clear using an example
Let’s say we’re getting some data from firebase and displaying it in the form of stats. Here’s the code for the same.
import React, {PureComponent} from "react";
import DisplayStat from "./displayStat.js"

class App extends PureComponent{
  constructor(){
    super();
    this.state={
      path : "path-1"
    }
  }
  
  changePath=()=>{
    this.setState({path: "path-2"});
  }
  
  render(){
    return(
      <div>
        <DisplayStat path={this.state.path} />
        <div onClick={this.changePath} >Change Path</div>
      </div>
    )
  }
}
import React, {PureComponent} from "react";

class DisplayStat extends PureComponent{
  constructor(props){
    super();
    this.state={
      firebaseRef: firebase.database().ref(this.props.path);
    }
  }
  componentDidMount() {
    this.getData(this.state.firebaseRef);
  }
  
  componentWillReceiveProps(nextProps){
    if(nextProps.path!==this.props.path){
      let {firebaseRef}=this.state;
      
      firebaseRef.off("value"); //Turn off the connection to previous path.
      
      firebaseRef=firebase.database().ref(nextProps.path);
      this.setState({firebaseRef, path :nextProps.path });
      this.getData(firebaseRef);
    }
  }

  getData=(ref)=>{
    // open connection and listen to firebase path
    ref.on("value", snapshot => {
      //Perform some operation
    });
  }
  
  render(){
    return(
      <div>
        //Display Stats
      </div>
    );
  }
}
The above example uses
componentWillReceiveProps
. Initially, the
displayStat.js
component will listen to firebase on
path-1
, when a user clicks on the Change Path button the state will change in the
App.js
file and
componentWillReceiveProps
will be called in the
displayStat.js
file. The previous connection to firebase path will be closed, and a new will get created. Notice we’re passing firebase reference as parameters to
getDate()
to listen to firebase.
Now let’s do the same thing using
getDerivedStateFromProps
 .
import React, {PureComponent} from "react";

class DisplayStat extends PureComponent{
  constructor(props){
    super();
    this.state={
      path: this.props.path,
      firebaseRef: firebase.database().ref(this.props.path);
    }
  }
  componentDidMount() {
    this.getData(this.state.firebaseRef);
  }
  
  componentDidUpdate(prevProps, prevState) {
    if (prevState.path !== this.state.path) {
      let firebaseRef=firebase.database().ref(this.state.path);
      this.setState({firebaseRef});
      this.getData(firebaseRef);
    }
  }
  
  static getDerivedStateFromProps(nextProps, prevState){
    if(nextProps.path!==prevState.path){
      let firebaseRef=prevState.firebaseRef;
      
      firebaseRef.off("value"); //Turn off the connection to previous path.
      
//       We can't do this here as we can't access `this` inside this method.
//       firebaseRef=firebase.database().ref(nextProps.path);
//       this.setState({firebaseRef, path :nextProps.path });
//       this.getData(firebaseRef);
      
      return {path : nextProps.path};
    }
    else return null;
  }

  getData=(ref)=>{
    // open connection and listen to firebase path
    ref.on("value", snapshot => {
      //Perform some operation
    });
  }
  
  render(){
    return(
      <div>
        //Display Stats
      </div>
    );
  }
}
Notice an object is being returned in the
getDerivedStateFromProps
to update the state and no class method is being called. We’re using
componentDidUpdate
to check if a path is changed or not and accordingly create a new firebase connection and listen to new path.
Thanks for reading this article. Please hit the Clap button if you like it.
Connect with me on LinkedIn.
You can also follow me on Twitter, Quora, and GitHub.

Tags

More by Amanshu Kataria

Topics of interest