cases you’re probably not going to run into any perf issues - More components means more bigger virtual DOMs, more nodes to reconcile in between changes
- If you’re currently working on a React app, there are prob optimizations you can make right now. There are probably inefficiencies there. - Doesn’t mean you should go after them - This stuff can be a time sink - Focus on building things for your users
in your UI or maybe you already did some profiling and your updates are taking way over 16ms and you’re shooting for 60fps. - Users already reported that your app feels slow - But also when figuring out whether or not this is a problem worth pursuing..
checking propTypes - If you have the Elements Pane or React dev tools open - There is overhead involved in repainting those things - These things make a huge difference in perceived performance - Don’t fall victim to following red herrings
is but the flame chart gives you a good feel for where the problem may be - Obviously in this case the 5ms are not the bottle neck - Dig into why React is doing all that work on the right
view. List all functions - Right now sorted by total - total is the time is took for that particular function and downstream function calls - If you sort by self that’ll tell you how long it took for just that function to run, not including any downstream function calls. - As you can see batchedUpdates is taking the longest which tells me React is spending a lot of time updating
we know the problem is related to React and there is no single function we wrote that is the bottle neck. - We can dig into React specific perf issues using React Perf Tools - These come with the React addons
should be able to memoize it just like I did fib - In case you’re new to React or aren’t super familiar with how it works at a high level, let’s quickly go over how changes in your DOM occur
a change comes in, React wants to figure out the minimal DOM operations that need to take place to update the DOM - It computes an updated virtual DOM, performs a diffs while figuring out what changes need to occur
vDOM - flushes those changes on the real DOM. Now the vDOM and real DOM match - Computing this diff and figuring out which imperative changes need to occur is expensive - React gives us a way to memoize our components so that it doesn’t spend time diffing nodes that haven’t changed. - There is a hook that lets us tell React, “you don’t need to spend time rendering me, I’m not going to change”
This is a method you implement on your component - Execute logics that determines whether or not - Default implementation returns true - This means that by default React will always re-render your components
And lets React focus on the part of the UI should update - Think of it as if you’re providing insider information to React so that it can more efficiently do its job
shouldComponentUpdate methods. Subtle bugs - Theoretically, you shouldn’t be passing props to your components that it doesn’t need to render. - So you should be able to simply check for equality of props and state within shouldComponentUpdate - React add ons ships with these two utilities - shallowCompare is just a function that does exactly what it says - PureRenderMixin implements a shouldComponentUpdate that uses shallowCompare
general solutions, why isn’t this the default? - The nature of JavaScript references types like Objects, arrays - With ref types, you can never really be certain that anything is the same or different. - Example: Array push, mutate obj in memory, the reference to it will never change - React doesn’t assume you’re using an immutable methodology so it can’t auto opt you into shallow comparisons
can still shoot yourself in the foot by accidentally breaking your shallow equal comparisons - There are a few things you can do to memoize effectively
! // Good <Item onClick={this.handeClick} /> - This is an example of what you don’t want to do. - Creating a new function on every render - If Item implements shouldComponentUpdate, then this.props.onClick will never be equal to nextProps.onClick
// Good <Item id={item.id} onClick={this.handleClick} /> ! ! - This is pretty much the same problem. - When you use bind inline like this, you’re still creating a new function on each render
// Good <Item id={item.id} onClick={this.handleClick} /> ! ! - One thing you can do - allow the component to pass the id to you via the callback - At HubSpot we use a mixin or decorator function to give us a memoized partial function.
// Good <Item onClick={this.partial( this.handleClick, item.id )} /> ! ! - Here the partial function is memoized with a cache backed by an immutable Map - So the first time it renders it’ll create my partial for me but on subsequent renders it’ll just return the same partial
// Good (defaultProps) <User friends={user.friends} /> - Another thing you don’t want to do is implement default props - If users.friends is not defined then we’re creating a new array on every render.
} ! // Good handleAddItem(item) { this.setState({ items: this.state.items.concat(item) }); } - This is a bit of a contrived example. - In the first example, we’re mutating an existing reference to an array so this.state.items is always equal to - In the second example, we’re using concat to create a new array - Yes coding in this style is slower but it’s a trade off - You’re trading that overhead of creating a new array for the ability pruning chunks of your UI at render time.
{ … } (b) => { … } - Break the function out into smaller memoized functions. - Even though the return value associated with combination of those arguments isn’t cached - You can still save cycles from having the individual computations cached.
value={this.props.todoInputValue} /> <ul> {this.props.todos.map(todo => ( <Todo todo={todo} /> ))} </ul> </div> ); } - Same applies with components - This example doesn’t have great abstraction boundaries - Every time `todoInputValue` changes, React will have to re-render all of the todos.
return foo; } https://github.com/facebook/react/issues/3226 - If you’re interested about the details around how the plugin figures out what’s constant, there’s some good discussion here
you may know, JSX usually compiles to `React.createElement` calls. - Those calls just spit out objects which are descriptions of your React elements - This plugin will turn this
bar, { type: Baz, props: { }, key: 'baz', ref: null } ] }, key: null, ref: null } - into this at build time - Saves you the calls to `React.createElement` at runtime
implementing some flavor of Flux where you have some components hooked up to a store. - You have some concept of a store - a way to hook up components to the store
ItemView - You might implement something like this - The problem with this is anyone of items changes in anyway, the shouldComponentUpdate implemented by ListView is going to break - It’ll have to iterate through all the items
1, text: ‘foo', … } Connect(ListView) ListView Connect(ItemView) - So an alternative that works especially nicely if your data is normalized on the client is to - Just pass ids to the ListView - Let item containers hold subscriptions to the store