React is known for performance but it doesnโt mean we can take things for granted. One of the key performance tips for faster React applications is to optimize your render function.
I have created a simple test where I compared the speed of the render()
function under several conditions:
- Stateless (functional) components vs Stateful (class) components
- Pure component rendering vs stateless components
- React 0.14 vs React 15 rendering performance in development and production
Key Takeaways
For those who are impatient and just want to read the results, hereโs the list of the most important learnings from this experiment:
- Stateless (functional) components are no faster than the stateful (class)
- Rendering in React 15 is roughly 25% faster compared to 0.14
- Pure components are the fastest. Use
shouldComponentUpdate
- Rendering in development mode is 2-8x slower than rendering in production
- Rendering in development with React 15 is about 2x slower than 0.14
Surprised?
As with every benchmark, understanding the methodology is the key to understanding the results. Letโs spend some time explaining what I did here and why.
How Performance Was Tested
The goal was to create a very simple test that iterates over the render()
function. I created the parent App component that contained one of the three types of components:
- A stateless component
- A stateful component
- A stateful component with
shouldComponentUpdate
returningtrue
// Stateful component class Stateful extends Component { render () { return <div>Hello Cmp1: stateful</div>; } } // Pure stateful with disabled updates class Pure extends Component { shouldComponentUpdate() { return false; } render () { return <div>Hello Cmp2: stateful, pure, no updates</div>; } } // Stateless component function Stateless() { return <div>Hello Cmp3: stateless</div>; }
Top level App component cycles through 100,000 renders for each of the three component types. Time to render was measured from the initial render to the last one using the browserโs native Performance.now functionalities. I couldnโt use Reactโs wonderful Perf utilities because they donโt work in production.
While props were always passed to ensure updates, target components kept the rendered data the same. That way we could ensure consistency with pure components. DOM is never updated from within these components to minimize interference with other APIs (e.g. layout rendering engine).
All of the JavaScript code ran in pure ES6 with no transpilation step(no transformation to ES5).
Tests were performed on Chrome 51 (with regular extensions such as various developer tools, 1Password, Pocket, etc), a clean Chrome Canary 54 (no plugins, no history, fresh) and Firefox 46. OS X 10.11 on a MBP sporting a 2.6 GHz Intel Core i7 processor made the host environment. All the numbers presented are average values from the runs in all browsers.
Precision when doing these types of benchmarks is never easy to achieve. Sometimes a test will run slower or faster, skewing results. Thatโs why I ran the tests several times and combined the results. Your results may vary.
The entire source code is in GitHub so please check it out. Youโll find a folder for each framework where you can run the usual npm i && npm start. The apps are on different ports too so they could be executed simultaneously. The readme file will clearly point that out.
Now that we have this covered, letโs talk about the findings.
Myth: Stateless Components are Faster
As of React 0.14 and React 15, stateless or functional components are just as fast as regular, class-based stateful components.
But how can stateless components not be faster when the entire lifecycle is stripped out, there are no refs, and there is no state to manage?
Stateless components are internally wrapped in a class without any optimizations currently applied, according to Dan Abramov.
@ggrgur There is no โoptimizedโ support for them yet because stateless component is wrapped in a class internally. It’s same code path.
โ Dan Abramov (@dan_abramov) July 19, 2016
Optimizations to stateless components have been promised and Iโm sure something will happen on that plan in one of the future versions of React.
The Simplest Performance Trick in React
Complex build optimizations aside, the crucial step in performance tuning for React applications is knowing when to render. And when not to.
Hereโs an example: letโs say youโre creating a drop-down menu and you want to expand it by setting state to expanded: true
. Do you have to render the entire block including all the list items inside the drop-down just to change that css value? Absolutely not!
shouldComponentUpdate is your friend, so use it. Do not use stateless components if you can optimize with shouldComponentUpdate
. You can currently streamline the process with shallowRender addon but I wouldnโt be surprised if this became part of the core functionality in React components. Does Smart Component or Pure Component ring a bell?
The original benchmarks compared rendering performance without any logic added to the render function. As soon as we add calculations the benefits of Pure components become even more apparent. We could think of it as a method of caching, too.
I donโt want to say that you should use shouldComponentUpdate
all over the place. Adding lots of logic or using it where components rarely output the same HTML code would just add the unnecessary burden. As with everything else, know the power of this lifecycle method and use it wisely.
Not rendering is just one side of the coin. What do the findings above tell us about improving rendering performance?
Smarter Rendering
The charts above showed the impact of the render()
function on your applicationโs performance. Render is the start of complex operations that eventually lead to optimized DOM updates.
The simpler the render, the faster the update:
- Less JavaScript code to processโโโparticularly important for mobile sites/apps
- Returning fewer changes will help speed up virtual DOM calculation
- Render of a parent component (container) will likely trigger
render()
of all of its children. And grandchildren. That means more computations.
Here are a few tips for optimizing the render phase:
- Skip render if possible
- Cache expensive computations in variables outside render functions
- โฆ or separate logic into multiple components and manage rendering selectively
- If possible, return early
- Keep
render()
slim (think functional programming) - Keep comparisons shallow
Just as your app may contain business logic computations inside the render phase, React also adds helpers to enhance your development experience. Letโs see how they impact performance.
Donโt Forget To Build for Production
By default, React sources are set to development mode. Not surprisingly, rendering in development environment is significantly slower.
The way to specify production mode when building your app is to define environment variable NODE_ENV=production. Of course, you want this only for production builds. Development counterparts will offer a much better debugging experience.
Hereโs how you would go about automating this variable in your Webpack configuration:
module.exports = { plugins: [ new webpack.DefinePlugin({ 'process.env': {NODE_ENV: '"production"'} }) ]
This will not just optimize render performance, but will also result in a much smaller bundle size.
Donโt forget to use the unavoidable UglifyJS plugin that also eliminates dead code.
plugins: [ new webpack.optimize.UglifyJsPlugin({ sourceMap: false, warnings: false, }), ],
We saw how React 15 in development mode is much slower compared to its predecessor. How do they compare in production?
React 15 is Faster
One of the most important updates in React 15 is the core change of how the framework interacts with DOM. innerHTML
was replaced with document.createElement
, a faster alternative for modern browsers.
Internet Explorer 8 no longer being supported likely means that some of the internal processes are more optimized.
React 15 truly is faster in the rendering process, but only when built for production. Development mode is actually quite a bit slower, mostly because of the plethora of functionalities that help debug and optimize code.
Note that these findings are based on React 15 with React-dom 15. The values may be significantly different in React Native development. Maybe you could run a similar test and share your results with the community.
Conclusion
Performance starts with optimizing the render block in React applications. This benchmark compares the three approaches to creating and optimizing components. Know when to use each and where each excels.
There could be many other ways of benchmarking React performance so definitely donโt take everything you learned here for granted. If you have other ideas please contribute.
Grgur Grisogono
Related Posts
-
React Navigation and Redux in React Native Applications
In React Native, the question of โhow am I going to navigate from one screen…
-
React Navigation and Redux in React Native Applications
In React Native, the question of โhow am I going to navigate from one screen…