Please do not use this in production. The project is still in development and not well tested.
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.
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
.
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
.
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.
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.
https://www.npmjs.com/package/babel-plugin-react-turbo
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.