From 97e8607897bc9280476ddf94ed47abad9969ef3c Mon Sep 17 00:00:00 2001 From: Christopher Chedeau Date: Wed, 11 Feb 2015 10:53:57 -0800 Subject: [PATCH] Advanced Performance Doc --- _data/nav_docs.yml | 2 + docs/10.8-perf.md | 1 + docs/11-advanced-performance.md | 207 +++++++++++++++++++++++++++ img/docs/should-component-update.png | Bin 0 -> 30920 bytes 4 files changed, 210 insertions(+) create mode 100644 docs/11-advanced-performance.md create mode 100644 img/docs/should-component-update.png diff --git a/_data/nav_docs.yml b/_data/nav_docs.yml index fcde3e37..beebf2d9 100644 --- a/_data/nav_docs.yml +++ b/_data/nav_docs.yml @@ -65,6 +65,8 @@ title: PureRenderMixin - id: perf title: Performance Tools + - id: advanced-performance + title: Advanced Performance - title: Reference items: - id: top-level-api diff --git a/docs/10.8-perf.md b/docs/10.8-perf.md index 0a460b2d..33f257c2 100644 --- a/docs/10.8-perf.md +++ b/docs/10.8-perf.md @@ -3,6 +3,7 @@ id: perf title: Performance Tools permalink: perf.html prev: pure-render-mixin.html +next: advanced-performance.html --- React is usually quite fast out of the box. However, in situations where you need to squeeze every ounce of performance out of your app, it provides a [shouldComponentUpdate](/react/docs/component-specs.html#updating-shouldcomponentupdate) hook where you can add optimization hints to React's diff algorithm. diff --git a/docs/11-advanced-performance.md b/docs/11-advanced-performance.md new file mode 100644 index 00000000..2d775190 --- /dev/null +++ b/docs/11-advanced-performance.md @@ -0,0 +1,207 @@ +--- +id: advanced-performance +title: Advanced Performance +permalink: advanced-performance.html +prev: perf.html +--- + +One of the first questions people ask when considering React for a project is whether their application will be as fast and responsive as an equivalent non-React version. The idea of re-rendering an entire subtree of components in response to every state change makes people wonder whether this process negatively impacts performance. React uses several clever techniques to minimize the number of costly DOM operations required to update the UI. + +## Avoiding reconciling the DOM + +React makes use of a *virtual DOM*, which is a descriptor of a DOM subtree rendered in the browser. This parallel representation allows React to avoid creating DOM nodes and accessing existing ones, which is slower than operations on JavaScript objects. When a component's props or state change, React decides whether an actual DOM update is necessary by constructing a new virtual DOM and comparing it to the old one. Only in the case they are not equal, will [React reconcile](http://facebook.github.io/react/docs/reconciliation.html) the DOM, applying as few mutations as possible. + +On top of this, React provides a component lifecycle function, shouldComponentUpdate, which is triggered before the re-rendering process starts, giving the developer the ability to short circuit this process. The default implementation of this function returns true, leaving React to perform the update: + +```javascript +shouldComponentUpdate: function(nextProps, nextState) { + return true; +} +``` + +Keep in mind that React will invoke this function pretty often, so the implementation has to be fast. + +Say you have a messaging application with several chat threads. Suppose only one of the threads has changed. If we implement shouldComponentUpdate on the ChatThread component, React can skip the rendering step for the other threads: + +```javascript +shouldComponentUpdate: function(nextProps, nextState) { + // TODO: return whether or not current chat thread is + // different to former one. +} +``` + +So, in summary, React avoids carrying out expensive DOM operations required to reconcile subtrees of the DOM by allowing the user to short circuit the process using shouldComponentUpdate, and, for those which should update, by comparing virtual DOMs. + +## shouldComponentUpdate in action + +Here's a subtree of components. For each one is indicated what shouldComponentUpdate returned and whether or not the virtual DOMs were equivalent. Finally, the circle's color indicates whether the component had to be reconciled or not. + +
+ +In the example above, since shouldComponentUpdate returned false for the subtree rooted at C2, React had no need to generate the new virtual DOM, and therefore, it neither needed to reconcile the DOM. Note that React didn't even had to invoke shouldComponentUpdate on C4 and C5. + +For C1 and C3 shouldComponentUpdate returned true, so React had to go down to the leaves and check them. For C6 it returned true; since the virtual DOMs weren't equivalent it had to reconcile the DOM. +The last interesting case is C8. For this node React had to compute the virtual DOM, but since it was equal to the old one, it didn't have to reconcile it's DOM. + +Note that React only had to do DOM mutations for C6, which was inevitable. For C8 it bailed out by comparing the virtual DOMs, and for C2's subtree and C7, it didn't even have to compute the virtual DOM as we bailed out on shouldComponentUpdate. + +So, how should we implement shouldComponentUpdate? Say that you have a component that just renders a string value: + +```javascript +React.createClass({ + propsTypes: { + value: React.PropTypes.string.isRequired + }, + + render: function() { + return
this.props.value
; + } +}); +``` + +We could easily implement shouldComponentUpdate as follow: + +```javascript +shouldComponentUpdate: function(nextProps, nextState) { + return this.props.value !== nextProps.value; +} +``` + +So far so good, dealing with such simple props/state structures is easy. We could even generalize an implementation based on shallow equality and mix it into components. In fact, React already provides such implementation: [PureRenderMixin](http://facebook.github.io/react/docs/pure-render-mixin.html). + +But what if your components' props or state are mutable data structures?. Say the prop the component receives, instead of being a string like 'bar', is a Javascript object that contains a string such as, { foo: 'bar' }: + +```javascript +React.createClass({ + propsTypes: { + value: React.PropTypes.object.isRequired + }, + + render: function() { + return
this.props.value.foo
; + } +}); +``` + +The implementation of shouldComponentUpdate we had before wouldn't always work as expected : + +```javascript +// assume this.props.value is { foo: 'bar' } +// assume nextProps.value is { foo: 'bar' }, +// but this reference is different to this.props.value +this.props.value !== nextProps.value; // true +``` + +The problem is shouldComponentUpdate will return true when the prop actually didn't change. To fix this we could come up with this alternative implementation: + +```javascript +shouldComponentUpdate: function(nextProps, nextState) { + return this.props.value.foo !== nextProps.value.foo; +} +``` + +Basically, we ended up doing a deep comparison to make sure we properly track changes. This approach is pretty expensive in terms of performance and it doesn't scale as we would have to write different deep equality code for each model. On top of that, it might not even work if we don't carefully manage object references. Say this component is used by a parent: + +```javascript +React.createClass({ + getInitialState: function() { + return { value: { foo: 'bar' } }; + }, + + onClick: function() { + var value = this.state.value; + value.foo += 'bar'; // ANTI-PATTERN! + this.setState({ value: value }); + }, + + render: function() { + return ( +
+ + Click me +
+ ); + } +}); +``` + +The first time the inner component gets rendered it will have `{ foo: 'bar' }` as the value prop. If the user clicks on the anchor, the parent component's state will get updated to `{ value: { foo: 'barbar' } }`, triggering the re-rendering process of the inner component, which will receive `{ foo: 'barbar' }` as the new value for the prop. + +The problem is that since the parent and inner components share a reference to the same object, when the object gets mutated on line 2 of the onClick function, the prop the inner component had will change. So, when the re-rendering process starts, and shouldComponentUpdate gets invoked, `this.props.value.foo` will be equal to `nextProps.value.foo`, because in fact, `this.props.value` references the same object as `nextProps.value`. + +Consequently, since we'll miss the change on the prop and short circuit the re-rendering process, the UI won't get updated from `'bar'` to `'barbar'`. + +## Immutable-js to the rescue + +[Immutable-js](https://github.com/facebook/immutable-js) is a Javascript collections library written by Lee Byron, which Facebook recently open-sourced. It provides *immutable** persistent* collections via *structural sharing*. Lets see what these properties mean: + +* *Immutable*: once created, a collection cannot be altered at another point in time. +* *Persistent*: new collections can be created from a previous collection and a mutation such as set. The original collection is still valid after the new collection is created. +* *Structural Sharing*: new collections are created using as much of the same structure as the original collection as possible, reducing copying to a minimum to achieve space efficiency and acceptable performance. If the new collection is equal to the original, the original is often returned. + +Immutability makes tracking changes cheap; a change will always result in a new object so we only need to check if the reference to the object has changed. For example, in this regular Javascript code: + +```javascript +var x = { foo: "bar" }; +var y = x; +y.foo = "baz"; +x === y; // true +``` + +Although y was edited, since it's a reference to the same object as x, this comparison returns true. However, this code could be written using immutable-js as follows: + +```javascript +var SomeRecord = Immutable.Record({ foo: null }); +var x = new SomeRecord({ foo: 'bar' }); +var y = x.set('foo', 'baz'); +x === y; // false +``` + +In this case, since a new reference is returned when mutating x, we can safely assume that x has changed. + +Another possible way to track changes could be doing dirty checking by having a flag set by setters. A problem with this approach is that it forces you to use setters and, either write a lot of additional code, or somehow instrument your classes. Alternatively, you could deep copy the object just before the mutations and deep compare to determine whether there was a change or not. A problem with this approach is both deepCopy and deepCompare are expensive operations. + +So, Immutable data structures provides you a cheap and less verbose way to track changes on objects, which is all we need to implement shouldComponentUpdate. Therefore, if we model props and state attributes using the abstractions provided by immutable-js we'll be able to use PureRenderMixin and get a nice boost in perf. + +## Immutable-js and Flux + +If you're using [Flux](http://facebook.github.io/flux/), you should start writing your stores using immutable-js. Take a look at the [full API](http://facebook.github.io/immutable-js/docs/#/). + +Let's see one possible way to model the thread example using Immutable data structures. First, we need to define a Record for each of the entities we're trying to model. Records are just immutable containers that hold values for a specific set of fields: + +```javascript +var User = Immutable.Record({ + id: undefined, + name: undefined, + email: undefined +}); + +var Message = Immutable.Record({ + timestamp: new Date(), + sender: undefined, + text: '' +}); +``` + +The object the Record function receives defines the fields the object has and their default values. + +The messages *store* could keep track of the users and messages using two lists: + +```javascript +this.users = Immutable.List(); +this.messages = Immutable.List(); +``` + +It should be pretty straightforward to implement functions to process each *payload* type. For instance, when the store sees a payload representing a new message, we can just create a new record and append it to the messages list: + +```javascript +this.messages = this.messages.push(new Message({ + timestamp: payload.timestamp, + sender: payload.sender, + text: payload.text +}); +``` + +Note that since the data structures are immutable, we need to assign the result of the push function to this.messages. + +On the React side, if we also use immutable-js data structures to hold the components' state, we could mix PureRenderMixin into all our components and short circuit the re-rendering process. diff --git a/img/docs/should-component-update.png b/img/docs/should-component-update.png new file mode 100644 index 0000000000000000000000000000000000000000..bc731d441e4a00e86b9ed3c5799874522a28a77f GIT binary patch literal 30920 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℞Eu94Y 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

bz{M(ff%}RqR9~B6D||a^zjGb9xrFlnzK;sJ^A9 zSu8xM#xBT23xw2?;t)E@6Mev&IviM_n=y!Qbhx&3jf{yzzD{7vP+#D%USuGd_4z_P z+?uAc@(`Cc(w3F+(cNWE38!L~^Vt84R(QxeO{Hpi`xxIIX7w1~!MJ_2x;?_a3bIwy zzUBPG%)&y3JR;aKAvML|N4J@x fT?~zO5!@Wf!vvx-|1U5V->e~Z6 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%0D2@KbY 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 QDq?_wtcpyflu6M40Rt3Yy8r+H literal 0 HcmV?d00001