6月18日、Codeminer42が「How React 19 (Almost) Made the Internet Slower」と題した記事を公開した。この記事では、React 19の新しい変更がどのようにしてインターネットの速度を遅くする可能性があったかについて詳しく紹介されている。
React 19が今年の初めに発表され、新機能や開発者体験(DX)の向上が話題となった。しかし、先週、React 19に加えられたある変更が多くのウェブサイトのパフォーマンスを大幅に低下させる可能性があることが明らかになった。
この問題は、Dominik(TkDodo)のツイートから始まった。
https://x.com/TkDodo/status/1800501040766144676
React 19では、同じSuspenseバウンダリ内の兄弟要素の並列レンダリングが無効化された。これにより、兄弟要素内でのデータ取得が直列に行われるようになり、データフェッチングのウォーターフォールが発生する。
以下はその例である。
https://github.com/facebook/react/pull/26380#issuecomment-2166178673
この変更は、パフォーマンスに重大な影響を与える可能性があるにもかかわらず、リリースノートには一行の箇条書きで簡単に記載されているだけだった。
そもそもSuspenseとは
ReactのSuspenseは、子コンポーネントが読み込みを終えるまでフォールバックを表示するコンポーネントである。これは、子コンポーネントが遅延ロードされる場合や、Suspense対応のデータ取得メカニズムを使用している場合に使用される。以下のように使用される。
<Suspense fallback={<Loading />}>
<ComponentThatFetchesDataOrIsLazyLoaded />
</Suspense>
Suspenseは以前からReactのAPIの一部であったが、長い間、唯一正式に認められた使用方法はReact.lazy
を用いたコンポーネントの遅延ロードであった。これは、アプリをコード分割し、必要なときにのみ分割部分をロードするために非常に有用である。
React.lazy
を使用する場合、初めて遅延ロードされたコンポーネントをレンダリングしようとすると、コンポーネントをラップしているSuspense
がトリガーされる。
そしてコンポーネントのコードの取得が完了するまでフォールバックがレンダリングされ、その後コンポーネント自体がレンダリングされる。
変更点の理解
React 18.3.1までは、同じSuspense内で複数のコンポーネントがある場合、兄弟要素は並列にレンダリングされていた。
しかしReact 19では、各データ取得は前のデータ取得が完了した後にのみ開始されるため、ウォーターフォールが発生していることがわかる。
以下のコード例では、React 18とReact 19の違いを示している。
function App() {
return (
<>
<Suspense fallback={"Loading..."}>
<ComponentThatFetchesData val={1} />
<ComponentThatFetchesData val={2} />
<ComponentThatFetchesData val={3} />
</Suspense>
</>
);
}
const ComponentThatFetchesData = ({ val }) => {
const result = fetchSomethingSuspense(val);
return <div>{result}</div>;
};
React 18の場合:
React 19の場合:
この新しい挙動は、データフェッチングとReact.lazy
の両方に影響を及ぼす。
これは次の PR が原因で発生する: https://github.com/facebook/react/pull/26380
結局、撤回!
幸いなことに、この変更は多くの反対意見を受け、Reactチームは一時的にこの変更を見送ることを決定した。よかった!
https://x.com/sophiebits/status/1801663976973209620
詳細はHow React 19 (Almost) Made the Internet Slowerを参照していただきたい。