From a2713ac71a667a93dcbcbdbace2d6d650966eefd Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Wed, 21 Feb 2018 00:44:10 +0100 Subject: [PATCH] Improve `Header` performance a bit (#3556) * Improve `Header` performance a bit I have been investigating `
` component performance once again today with `render` cycles in particular. I have observed that during `push` phase, a typical Header in NavigationPlayground re-renders 3 times on iOS and 2 times on Android. The first time is obvious on both platforms since that's when we add a new scene to an array. Second on iOS was also self-explanatory - we measure title width and other layout parameters in order to provide a better animation. What got me thinking was the last render cycle that didn't have a clear origin. After digging around I've found it is caused by a `scenes` array changing when transition finishes. What is surprising, it's just the reference to the object that changes, the content (items inside) stay the same. I have found out that this is caused by `Transitioner` looping through an array of scenes attempting to remove stale scenes. Since scene becomes stale when you `pop` from it, this is obviously a noop when `pushing` a new route. That, obviously, causes an extra render cycle since `filter` produces same, but with a different pointer, object. * Update Transitioner.js * Update Transitioner.js --- packages/react-navigation/src/views/Transitioner.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/react-navigation/src/views/Transitioner.js b/packages/react-navigation/src/views/Transitioner.js index 051f874b..b64c3d87 100644 --- a/packages/react-navigation/src/views/Transitioner.js +++ b/packages/react-navigation/src/views/Transitioner.js @@ -179,9 +179,19 @@ class Transitioner extends React.Component { const prevTransitionProps = this._prevTransitionProps; this._prevTransitionProps = null; + const scenes = this.state.scenes.filter(isSceneNotStale); + const nextState = { ...this.state, - scenes: this.state.scenes.filter(isSceneNotStale), + /** + * Array.prototype.filter creates a new instance of an array + * even if there were no elements removed. There are cases when + * `this.state.scenes` will have no stale scenes (typically when + * pushing a new route). As a result, components that rely on this prop + * might enter an unnecessary render cycle. + */ + scenes: + this.state.scenes.length === scenes.length ? this.state.scenes : scenes, }; this._transitionProps = buildTransitionProps(this.props, nextState);