Skip to content

Instantly share code, notes, and snippets.

@ethan-deng
Last active December 16, 2016 19:59
Show Gist options
  • Save ethan-deng/c5008d8c1bc4bd3c8f799a0c53c92b11 to your computer and use it in GitHub Desktop.
Save ethan-deng/c5008d8c1bc4bd3c8f799a0c53c92b11 to your computer and use it in GitHub Desktop.

React Performance

Immutable.js

To summarize, the performance managment of React is about using shouldComponentUpdate with "Deep Change" and "Shallow Change" of state object.

First, when implementing shouldComponentUpdate, we always want to do shallow comparason only, since deep comparason is expensive.

Second, based on the first assumption, when we do "Deep Change" for example when we change an array in place using array push, pop, splice ... or change an object property value, the state objecct's reference never change and since the shouldComponentUpdate will be blind to these changes and thus it will always return false.

Third, based on the first assumption, when we do "Shallow Change" of state object which is change the reference of state variable such as using array concat, slice, map, filter ... or when assign state object to a new object, shouldComponentUpdate will always return true which defeats the purpose of having to define shouldComponentUpdate.

In practice when using Flux store without immutable.js, if most of the Flux actions will result in the change of the most of UI, these actions will (and should) do a "Shallow Change" of the state in store. These will result in most of shouldComponentUpdate close to the root level will return true.

When "Deep Change" is necessary (which is considered anti-pattern): For example adding an item to a large data set retrieved from a server, we may not want to retrieve the whole data set again since we could mutate the large data set on the client. The potential issue is that shouldComponentUpdate will be blind to these changes. Once solution is that to always company a "Shallow change" to a "Deep Change" which will help to trigger DOM tree update.

How much immutable.js can help?

For actions result in "Shallow change", immutable.js will help to improve performance by reducing number of UI change needed. However if we expect most these actions should change the UI, then only a small number of action will see a boost in performance.

For actions result in "Deep change", when using immutable shouldComponentUpdate will be aware of these changes and no companying "Shallow change" will needed (but in practice there will be necessary shallow changes anyway), shouldComponentUpdate will know if the "Deep Change" really change the state or not. However we will expect most of the "Deep Change" will change the state, and thus immutable.js will only boost very small number of actions.

So in practice, I think we may not really need to use immutable.js to boost performance. We could

  1. Implement most actions to do "Shallow change" and always trigger updates of components close to root level. (Even we implement shouldComponentUpdate in those components).

  2. At leaf level, be careful with the props and state of the component and implement shouldComponentUpdate in those components. The will help boost performance without re-rendering these components.

  3. Break up root components to use child components based on point 1 and 2.

  4. Give special treatment to components may have performance issue. For example for a table with a lot of rows, if we know that when a dialog is open, the table will never need update then we can use a flag from store to implement shouldComponentUpdate. However if we are careful with the props a component can receive then this may not necessary.

  5. Be care with the props a component will receive, especailly when using {...this.props} to pass the props down.

Implication for Flux framework

For Flux framework such as Alt, When passing the Alt store as prop to component, when an Alt action is triggerred, Alt just make a shallow copy of the store object. So if you implement shouldComponentUpdate as React PureRenderMixin, then you will always get a true as result and result in the UI update. In order to implement a useful shouldComponentUpdate, you have to pass variables represent the state inside the store to components.

However it is still okay to pass the Alt store directly to the root level component or component close to root level since if 99% of the actions will still trigger a UI updates. Just be careful when passing props to the leaf leave components and when it is possible pass just the state inside the store but not the store itself.

Also see these two links which boost the performance without using immutable.js

Shallow! Shallow! Shallow! Do things shallow.

  • Do shallow compare in shouldComponentUpdate. This is the default implementation when using PureRenderMixin and there is not much you need to do.

  • Do shallow copy of state after making deep changes. The shallow copy of object is actually act as a flag to shouldComponentUpdate that something has changed. It is not much difference to just using a varialbe to flag the change. use ES7 sytax this.myStoreData = {...this.myStoreData, p1: 'new value'};

  • If a dynamic object must be passed as props to child component, convert it to a string.

  • Do not pass a function definition directly into child component but pass a name of function definition instead.

  • Be careful with inline style. When passing inline style down as prop to child component, it passes a new object for every render. Define inline style in the child component.

  • Use with 3rd party component. Since there is no way that 3rd party component can implement shouldComponentUpdate (because if it doesn't update then your component as its child will not update), instead of wrapping the 3rd component around your component, your component should wrap around it. In this way, it is your component will control shouldComponentUpdate. Update: Actually it should be the other way around. The 3rd party component may inject prop to its child components which may be using dynamic object such as "style". Your component must be wrapped inside the 3rd party component to avoid the injected props.

React performance is crucial for animation such as drag and drop.

Design Consideration for Components

Be careful what the props will be passed to child component. Never create a new object and pass the new object to the component. Always use a reference to an object in the store and pass the reference to child component.

Do let the child components decide what they are. Always tell the child component what they are. For exmaple, when creating a data row component of a table, if you pass the index of the row to the component and when a new item is appended to the top of the list, the index of every row will change. Instead decide if the row is current selected row outside the child component and pass the indicator as prop to the component.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment