The ultimate solution for React performance optimization
React Turbo (beta)
The ultimate solution for React performance optimization
react-turbo transforms your React apps to have truly fine-grained reactivity, so you don’t need to worry about React performance optimization anymore. Read this article to know more.
Installatoin
react-turbo comes with a babel plugin, so the prerequisite is react-app-rewired. If you’ve installed it, you can skip and jump to install react-turbo.
Install react-app-rewired and customize-cra
Follow the steps of react-app-rewired and customize-cra.
npm i --save-dev react-app-rewired customize-cra
Modify package.json, add config-overrides.js and .babelrc.
Install react-turbo and the plugin
npm i react-turbo
npm i --save-dev babel-plugin-react-turbo
Modify .babelrc
{
"plugins": ["babel-plugin-react-turbo"]
}
You can see all changes of installation in this commit.
Example
function expensiveRandom(a) {
let k = 0;
for (let i = 0; i < 10_000_000; i++) k += Math.random();
console.log('expensiveRandom:', k);
return a;
}
function App() {
const [a, setA] = useState(1000);
const [b, setB] = useState(1000);
return (
<div className="App">
<input value={a} type="number"
onChange={(e) => setA(parseInt(e.target.value))}/>
<span>{expensiveRandom(a)}</span>
<input value={b} type="number"
onChange={(e) => setB(parseInt(e.target.value))}/>
<span>{b}</span>
</div>
);
}
expensiveRandom contains some expensive calculation. When a or b changes, the component will re-render. The rendering will be stuck because it hits <span>{expensiveRandom(a)}</span>.
With react-turbo, when b changes, only the elements that depend on b will re-render (<input value={b} and <span>{b}</span>), so expensiveRandom(a) won’t be executed.
Note that, react-turbo happens in compile time with babel, so you don’t need to modify any code to get it work.
babel-plugin-react-turbo
https://www.npmjs.com/package/babel-plugin-react-turbo
Details
The above example will be compiled to something like
function App() {
const [$a, setA] = useAtom(1000);
const [$b, setB] = useAtom(1000);
return (
<div className="App">
<Controller a={$a}>
{({a}) => (
<input value={a} type="number"
onChange={(e) => setA(parseInt(e.target.value))}/>
)}
</Controller>
<Controller a={$a}>
{({a}) => <span>{expensiveRandom(a)}</span>}
</Controller>
<Controller b={$b}>
{({b}) => (
<input value={b} type="number"
onChange={(e) => setB(parseInt(e.target.value))}/>
)}
</Controller>
<Controller b={$b}>
{({b}) => <span>{b}</span>}
</Controller>
</div>
);
}
So no matter how fast b changes, the elements depending on a won’t re-render.
$a and $b are observable atoms like Recoil, Jotai or effector.