Optimizing rendering React components

MobX is very fast, often even faster than Redux. But here are some tips to get most out of React and MobX. Note that most tips apply to React in general and are not specific for MobX.

Use many small components

@observer components will track all values they use and re-render if any of them changes. So the smaller your components are, the smaller the change they have to re-render; it means that more parts of your user interface have the possibility to render independently of each other.

Render lists in dedicated components

This is especially true when rendering big collections. React is notoriously bad at rendering large collections as the reconciler has to evaluate the components produced by a collection on each collection change. It is therefore recommended to have components that just map over a collection and render it, and render nothing else:

Bad:

@observer class MyComponent extends Component {
    render() {
        const {todos, user} = this.props;
        return (<div>
            {user.name}
            <ul>
                {todos.map(todo => <TodoView todo={todo} key={todo.id} />)}
            </ul>
        </div>)
    }
}

In the above listing React will unnecessarily need to reconcile all TodoView components when the user.name changes. They won't re-render, but the reconcile process is expensive in itself.

Good:

@observer class MyComponent extends Component {
    render() {
        const {todos, user} = this.props;
        return (<div>
            {user.name}
            <TodosView todos={todos} />
        </div>)
    }
}

@observer class TodosView extends Component {
    render() {
        const {todos} = this.props;
        return <ul>
            {todos.map(todo => <TodoView todo={todo} key={todo.id} />)}
        </ul>)
    }
}

Don't use array indexes as keys

Don't use array indexes or any value that might change in the future as key. Generate id's for your objects if needed. See also this blog.

Dereference values lately

When using mobx-react it is recommended to dereference values as late as possible. This is because MobX will re-render components that dereference observable values automatically. If this happens deeper in your component tree, less components have to re-render.

Fast:

<DisplayName person={person} />

Slower:

<DisplayName name={person.name} />.

There is nothing wrong to the latter. But a change in the name property will, in the first case, trigger the DisplayName to re-render, while in the latter, the owner of the component has to re-render. However, it is more important for your components to have a comprehensible API than applying this optimization. To have the best of both worlds, consider making smaller components:

const PersonNameDisplayer = observer(({ props }) => <DisplayName name={props.person.name} />)

Bind functions early

This tip applies to React in general and libraries using PureRenderMixin especially, try to avoid creating new closures in render methods.

See also these resources:

Bad:

render() {
    return <MyWidget onClick={() => { alert('hi') }} />
}

Good:

render() {
    return <MyWidget onClick={this.handleClick} />
}

handleClick = () => {
    alert('hi')
}

The bad example will always yield the shouldComponent of PureRenderMixin used in MyWidget to always yield false as you pass a new function each time the parent is re-rendered.