Now that the much anticipated React Hooks API has been officially released, I was finally able to scratch the itch of comparing its execution speed compared to good old HOCs. And the results surprised me!
Amidst all the excitement over shiny new Hooks, the trusty old HOC may have been unnecessarily vilified. Right off the bat I will say that my simple benchmarks showed HOCs may still be faster, despite the “wrapper hell” that it’s been recently much maligned for. Of course, if you find that my test is flawed, I will appreciate being corrected.
I contrived a basic test app that renders 10,000 instances of a functional component that has 3 state values, and an effect that sets the 3 state values once after the first render. The main component logs the time elapsed from instantiation of the root component to the time it finishes rendering the 10,000 items. For this, it also uses an effect.
I then created 2 versions of the test app, one using Hooks and the other using HOCs (using the Reactor Library that I recently published: https://github.com/arnelenero/reactorlib#reactor-library).
import React, { useEffect, useState } from 'react';import { render } from 'react-dom';
const array = [];for (let i = 0; i < 10000; i++) array[i] = true;
const Component = () => {const [a, setA] = useState('');const [b, setB] = useState('');const [c, setC] = useState('');
useEffect(() => {setA('A');setB('B');setC('C');}, []);
return <div>{a + b + c}</div>;};
const Benchmark = ({ start }) => {useEffect(() => {console.log(Date.now() - start);});
return array.map((item, index) => <Component key={index} />);};
render(<Benchmark start={Date.now()} />, document.getElementById('root'));
import React from 'react';import { render } from 'react-dom';import { compose, withState, withEffect } from '@reactorlib/core';
const array = [];for (let i = 0; i < 10000; i++) array[i] = true;
const _Component = ({ a, b, c }) => {return <div>{a + b + c}</div>;};
const Component = compose(withState({a: '',b: '',c: ''}),withEffect(({ setA, setB, setC }) => {setA('A');setB('B');setC('C');}, true))(_Component);
const _Benchmark = () => {return array.map((item, index) => <Component key={index} />);};
const Benchmark = compose(withEffect(({ start }) => {console.log(Date.now() - start);}))(_Benchmark);
render(<Benchmark start={Date.now()} />, document.getElementById('root'));
Tests were run on an early 2015 12" Macbook (1.1GHz Core M, 8GB RAM) running MacOS Sierra 10.12.3 and Chrome 71.
Both versions are on React 16.8.1.
I captured the results of 10 test runs on MacOS Chrome, all of which show a clear winner.
Rendering Time in milliseconds
Run# Hooks HOCs-----------------------------1 2197 14402 2302 17573 2749 14074 2243 13095 2167 16446 2219 15167 2322 16738 2268 16309 2164 144610 2071 1597
But don’t take my word for it. You can run the two versions I posted above and see for yourself.
If the difference in rendering speed would be crucial to your use case (e.g. looped instances of component), these figures show that you may have to reconsider using HOCs instead. For typical use cases, though, the difference may not be significant enough to sway your decision.
The choice between Hooks and HOC might not be the no-brainer that we initially thought it to be. Apart from what I covered here, there are other factors that affect this choice, such as your specific strategy for code reuse. Good thing is, we are not forced to pick just one of them; we can use both, with careful judgment on which components should prefer one over the other.
In spots where you would need HOCs instead, you might want to check out my Reactor Library (https://github.com/arnelenero/reactorlib#reactor-library). No, it’s not only about functional components; it has a whole lot of other things that can help you simplify your React/Redux development. Feel free to use, it’s yours as much as mine.