/react-turbo

Primary LanguageTypeScriptMIT LicenseMIT

React Turbo (beta)

npm npm bundle size npm type definitions GitHub

Please do not use this in production. The project is still in development and not well tested.

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.

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.