The lifetime of React properties

Dec 10, 2015  

Did you ever wonder what happens to a component’s props when React instantiates the component class? And then, what happens to props if the parent re-renders the component with different properties?

The spy component

Let’s create a spy so that we can check what’s going on.

import React from 'react';
import shallowCompare from 'react-addons-shallow-compare';

let spyConstructors = [];
let spyRenders = [];

export default class Spy extends React.Component {
  constructor (props) {
    super (props);
    spyConstructors.push ({obj: this, props: this.props});
  }
  shouldComponentUpdate (nextProps, nextState) {
    return shallowCompare (this, nextProps, nextState);
  }
  getText () {
    return 'Text:' + this.props.text;
  }
  render () {
    spyRenders.push ({obj: this, props: this.props});
    return <div id={this.props.id}>{this.props.text}</div>;
  }

  static clear () {
    spyConstructors = [];
    spyRenders = [];
  }

  static getConstructorLog () {
    return spyConstructors;
  }

  static getRenderLog () {
    return spyRenders;
  }
}

The test code

We’ll use the <Spy> like so:

Spy.clear ();
ReactDOM.render (<Spy text='a'/>, mountNode);
ReactDOM.render (<Spy text='b'/>, mountNode);
const constr = Spy.getConstructorLog ();
const render = Sky.getRenderLog ();

and observe what happens to the props which get passed into the constructor, and then used by the render() method.

Results

In my test code, there will be only one component instanciation. React is smart enough to reuse the same <Spy> element. We can check this with:

expect (constr).to.have.length (1);

And naturally, there will be two calls to render(), since shouldComponentUpdate will return true when switching from text='a' to text='b':

expect (render).to.have.length (2);

We can verify that the same component instance was used:

expect (constr[0].obj).to.equal (render[0].obj);
expect (constr[0].obj).to.equal (render[1].obj);

And what about the props?

Well, the props change between the first and the second call to ReactDOM.render, so the element’s props will be replaced:

expect (constr[0].props).to.equal (render[0].props);
expect (constr[0].props).to.not.equal (render[1].props);

That’s about what we should expect. Changing the properties should indeed inject other props into the <Spy>, and this is what happens.

React provides a lifecycle method called componentWillReceiveProps() which gets called before the props change. See React component specs for further the details.