Codementor Events

Speed up your React Components with flattened states and React.PureComponent

Published Aug 10, 2018
Speed up your React Components with flattened states and React.PureComponent

More often than not I see React Components written like this:

class MyComponent extends React.Component {
  constructor(props) {
    this.state = {
      data: props.data /* Where data is an object */
    };
  }

Nesting objects in the state make it difficult to detect if anything really did change in props.data. And if we don't both checking if data has really changed we are possibly rendering our Component (and thus the children below it), for no good reason. Consider this:

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
  }

  componentDidMount() {
    setInterval(() => {
      this.setState({
        count: 0
      });
    }, 1000)
  }

  render() {
    console.log(this.state); // Added this only to illustrate the component behaviour. Avoid console.logs in render() method at all times in production.
    return (
      <div className="App">
        Unrelated div text
      </div>
    );
  }
}

Yes! this.state.count is not even being referenced in the render(). Yet, on checking on the console, you'll find render() methods being called repeated even if the data never really changed. Imagine such a component with hundreds of children. Each one, triggering render for absolutely no good reason.

Just make the above to a React PureComponent

- class MyComponent extends React.Component {
+ class MyComponent extends React.PureComponent {

You won't find repeated rendering this time. If this was a large component, you avoided tons of re-renders.

How it works

React.PureComponent does a simple shallow check on all its props and state behind the scenes.

  if (ctor.prototype && ctor.prototype.isPureReactComponent) {
    return (
      !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
    );
  }

shallowEqual checks every property on the old object against the new one. (For the curious lot, the source can be found here 😉 )

Back to our original problem: nested objects in state

Try running the following on CodeSandbox.


// Assume App was rendered as <App data={data} /> This has already been done for you in the sandbox link above.

class App extends React.PureComponent {

  constructor(props) {
    super(props);
    this.state = {
      data: props.data
    }
  }

  componentDidMount() {
    setInterval(() => {
      this.setState({
        data: {
          foo: 1,
          bar: 2
        }
      })
    }, 1000)
  }

  render() {
    console.log(this.state)
    return (
      <div className="App">
        <h1>Hello CodeSandbox</h1>
        <h2>Start editing to see some magic happen!</h2>
      </div>
    );
  }
}

Logs flooding the console. Which means render was called repeatedly.

Now make the following two changes.

-    this.state = {
-      data: props.data
-    }

+    this.state = {
+      ...props.data
+    }

and in componentDidMount

-    setInterval(() => {
-      this.setState({
-        data: {
-          foo: 1,
-          bar: 2
-        }
-      });
-    }, 1000);
+    setInterval(() => {
+      this.setState({
+        foo: 1,
+        bar: 2
+      });
+    }, 1000);

A sandbox has been created for you already if you'd like to use that. No more repeated component re-renders. Think about how many components re-renders have been avoided this way when data hasn't really changed 😃

So use make your state shallow and enjoy the benefits of React.PureComponent

Discover and read more posts from Manas Jayanth
get started