How will React 0.14's Stateless Components offer performance improvements without shouldComponentUpdate? How will React 0.14's Stateless Components offer performance improvements without shouldComponentUpdate? reactjs reactjs

How will React 0.14's Stateless Components offer performance improvements without shouldComponentUpdate?


Since your component is just a pure function of its parameters, it would be straightforward to cache it. This is because of the well known property of pure functions, for same input they will always return same output. Since they only depend on their parameters alone, not some internal or external state. Unless you explicitly referred some external variables within that function that might be interpreted as a state change.

However, caching would not be possible if your function component reads some external variables to compose the return value, so that, those external variables might change over time, making cached value obsolete. This would be a violation of being a pure function anyways and they wont be pure anymore.

On React v0.14 Release Candidate page, Ben Alpert states:

This pattern is designed to encourage the creation of these simple components that should comprise large portions of your apps. In the future, we’ll also be able to make performance optimizations specific to these components by avoiding unnecessary checks and memory allocations.

I am pretty sure that he meant cacheability of pure functional components.

Here is a very straight forward cache implementation for demonstration purposes:

let componentA = (props) => {  return <p>{ props.text }</p>;}let cache = {};let cachedA = (props) => {  let key = JSON.stringify(props); // a fast hash function can be used as well  if( key in cache ) {    return cache[key];  }else {    cache[key] = componentA(props);    return cache[key];  }}

And there are other good properties of pure functional components that I can think of at the moment:

  • unit test friendly
  • more lightweight than class based components
  • highly reusable since they are just functions


Avoiding Unnecessary Allocations

If I understand correctly, stateless functional components are converted into regular components. From the source:

function StatelessComponent(Component) {}StatelessComponent.prototype.render = function() {  var Component = ReactInstanceMap.get(this)._currentElement.type;  return Component(this.props, this.context, this.updater);};

When an instance of a stateless component is created, a new object is allocated. This new object has lifecycle methods such as componentWillMount and componentWillReceiveProps. I'm guessing that the plan is to not create these objects at all. Not creating the objects will avoid unnecessary allocations.

Avoiding Unnecessary Checks

Implementing the lifecycle methods requires a number of checks like this:

if (inst.componentWillUpdate) {  inst.componentWillUpdate(nextProps, nextState, nextContext);}

Stateless functional components can be assumed to not have these lifecycle methods. That could be what the docs are referring to, but I'm not sure.

EDIT

Removed stuff on memoization, which didn't answer the question or explain stuff well.


You can use a decorator to compose your stateless function components to perform high order optimisation to determine if React should renders this component or not. I'm using immutable to perform strict equality checks between props.

Let's say we have this kind of component :

ClickableGreeter.js

const ClickableGreeter = (props) => (    <div onClick={(e) => props.onClick(e)}>        {"Hello " + props.name}    </div>)ClickableGreeter.propTypes = {    onClick: React.PropTypes.func.isRequired,    name: React.PropTypes.text.isRequired}export default ClickableGreeter;

We want React to not rendering it if the name does not change. I'm using a simple decorator that use immutable library to create immutable representation of props and nextProps and perform a simple equality check :

pureImmutableRenderDecorator.js:

import React from 'react'import Immutable from 'immutable';const pureComponent = (Component, propsToRemove = []) => {    class PureComponent extends React.Component {        constructor(props) {            super(props);            this.displayName = 'PureComponent';        }        comparator(props, nextProps, state, nextState) {            return (                !Immutable.is(Immutable.fromJS(props), Immutable.fromJS(nextProps)) ||                !Immutable.is(Immutable.fromJS(state), Immutable.fromJS(nextState))            )        }        removeKeysFromObject(obj, keys) {            var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target;        }        shouldComponentUpdate(nextProps, nextState) {            let propsToCompare = this.removeKeysFromObject(this.props, propsToRemove),                nextPropsToCompare = this.removeKeysFromObject(nextProps, propsToRemove);            return this.comparator(propsToCompare, nextPropsToCompare, this.state, nextState)        }        render() {            return <Component {...this.props} {...this.state} />        }    }    return PureComponent;}export default pureComponent;

Then, you can create a PureClickableGreeter component by doing :

const PureClickableGreeter = pureComponent(ClickableGreeter, ['onClick']) //we do not want the 'onClick' props to be compared since it's a callback

Of course, using immutable here is overkill because it's a simple string comparison but as soon as you need some nested props, immutable is the way to go. You should also keep in mind that Immutable.fromJS() is a heavy operation, but it's fine if you do not have to many props (and that's normally the whole point of stateless functional components : to keep as many props as possible for better code splitting and reusability).