React 18では何が登場しますか?
React 18は、人気のJavaScriptコンポーネントライブラリの次のメジャーバージョンです。現在、リリース候補として利用可能で、データ取得、パフォーマンス、サーバーサイドレンダリングを改善するためのいくつかの変更を導入しています。
すべての機能を活用するには、プロジェクトをアップグレードする必要があり、いくつかの互換性のない変更が発生する可能性があります。ただし、React 18は一般的に古いコードと後方互換性があります。リリースバージョンを上げることができるはずです。
package.jsonあまり多くの即時の問題に直面することなく。
同時描画
React 18の多くの改訂の背後にある動機は「同時レンダリング」と呼ばれるものに関係しています。このメカニズムにより、Reactはコンポーネントツリーの複数のバージョンを同時に組み立てる方法を持つことができます。その詳細はライブラリの内部にのみ関連していますが、その結果、アプリの柔軟性が向上し、パフォーマンスが強化されます。
並行レンダリングはレンダリングプロセスを中断可能にします。React 17のレンダリングは、一度開始されると完了するまで実行する必要がありますが、React 18は途中で一時停止し、後で再開する方法を提供します。
この能力は、Reactのレンダリングが全体的なブラウザのパフォーマンスに影響を与える可能性が低くなることを意味します。これまで、レンダリングが進行している間、キー入力やペイントなどのブラウザのイベントはブロックされていました。同時レンダリングが有効になることで、キー入力がレンダリングを中断し、ブラウザが変更を処理できるようになり、その後レンダリングが再開されます。
これにより、他のアクティビティとのレンダリングが一致する際にカクつくことが少ない、よりスムーズなユーザー体験が実現します。Reactは複数の作業ブランチを維持しており、1つのブランチのレンダリングが完了すると、それが目に見えるUIを生成するメインブランチに受け入れられます。このプロセスは数回の異なるセッションにわたって発生することがあります。
同時モードと同時レンダリング
React 18がアルファ版に達する前、この機能は「同時モード」と呼ばれていました。古い記事やドキュメントではまだこの名前を見ることがあるかもしれません。同時レンダリングの概念はReact 18では独立したモードとしては存在しなくなりました。これにより、既存のアプリケーションが新しいアプローチに移行しやすくなります。
並行描画は、既存の描画システムとは根本的に異なります。これは、親しみのあるAPIを置き換えるまったく新しいAPIを備えています。
ReactDOM.render()かつての同時モードの頃、同時実行は全か無かでした:アプリに対して有効化されるか、重大な破壊的変更の可能性があるか、あるいは完全に禁止されていました。今では、Reactが実際に同時実行機能を必要とするDOMの更新にのみ同時レンダリングを適用することで、より優雅に処理されています。
新しいルートAPI(同時モードを有効にする)
React 18にアップグレードされた既存のアプリは使用を続けることができます
ReactDOM.render()今後しばらくの間、このため、あなたのアプリは並行処理サポートなしで、v17からの馴染みのあるレンダラーを使用します。APIがもはやサポートされていないというコンソール警告が表示されますが、アップグレード中は無視しても構いません。
import App from "./App.js";
import ReactDOM from "react-dom";
// "ReactDOM.render is no longer supported in React 18"
ReactDOM.render(<App />, document.getElementById("root"));
警告を解除するには、新しいものに切り替えてください。
createRoot()API :
import {createRoot} from "react-dom/client";
const root = createRoot(document.getElementById("root"));
root.render(<App />);
createRoot()新しいルートオブジェクトを返し、それはReactのレンダリングサーフェスを表します。あなたはそれを呼び出すことができます
render()ルートにReactコンポーネントをレンダリングする方法。上記のコードの結果は以前と同じです。
ReactDOM.render()例。
createRoot()よりオブジェクト指向のインターフェイスで、使いやすさが向上しています。
によって生成された根
createRoot()
同時レンダリングをサポートします。このAPIにアップグレードすると、React 18の新しい機能にオプトインでアクセスできるようになります。
その
createRoot()相当する
ReactDOM.unmountComponentAtNode()は新しいです
unmount()ルートオブジェクトで公開されたメソッドです。これを使って、Reactコンポーネントツリーを切り離し、アプリのレンダリングを停止することができます:
import App from "./App.js";
import ReactDOM from "react-dom";
// OLD
ReactDOM.unmountComponentAtNode(document.getElementById("root"));
// NEW
const root = createRoot(document.getElementById("root"));
root.unmount();
同時機能
同時レンダリングを使用すると、アプリのパフォーマンスを向上させるための同時機能を利用できます。ここに利用できる主要なAPIのいくつかがあります。
サスペンス
その
<Suspense>コンポーネントはReact 16から存在しています。それにより、条件が満たされるまでコンポーネントの子供がレンダリングされるのを防ぐことができます。データの取得や非同期モジュールのインポートに一般的に使用されます。
const fetchPostHistory = id => fetch(`/users/${id}/posts`);
const UserCard = ({Id, Name}) => {
const [postHistory] = useState(() => fetchPostHistory(Id));
<div>
<h1>{Name}</h1>
<React.Suspense fallback="Loading...">
<UserPostHistoryList posts={postHistory} />
<ReportUserLink id={Id} />
</React.Suspense>
</div>
};
この例では、どちらも
UserPostHistoryまたは
ReportUserLinkコンポーネントは、ユーザーの投稿履歴データがネットワークから取得されるまで表示されません。これは多くの状況でうまく機能していますが、React 17の実装にはいくつかの癖があります。
各コンポーネントの効果を記録すれば、あなたは次のことを見るでしょう。
ReportUserLinkコンポーネントは、投稿がまだ読み込まれている間にレンダリングされましたが、その時点では目に見えませんでした。以前の同時実行性の説明を使うと、理由を説明することができます:Reactがコンポーネントツリーのレンダリングを開始した時点で、それを止める方法がなかったのです。人間がそれを見つけることができるとしてもです。
ReportUserLinkまで冗長です
postHistorypopulated.
React 18ではサスペンスがより強力になりました。新しいバージョンは「同時サスペンス」と呼ばれ、以前の実装は「レガシーサスペンス」と呼ばれています。これにより、上記の例の問題を解決します。同じコードを同時実行を有効にしてレンダリングすると、レンダラーが到達するのを防ぎます。
<ReportUserLink>データ取得が進行中の間。
各コンポーネントの影響を記録することで、それが示されるでしょう。
ReportUserLinkポスト履歴が利用可能になるまで、一度だけコミットされます。Reactは、レンダリングが達成されると中断します。
UserPostHistoryListデータがロードされるのを待つ必要があります。ネットワーク呼び出しが完了すると、ReactはSuspenseサブツリーの残りの部分のレンダリングを再開します。
この機能は、ユーザーが決して恩恵を受けない無駄な作業を避けるのに役立ちます。また、コンポーネントが予想よりも早く効果を実行する可能性があるサスペンスに関するいくつかの問題も解決します。最後に、この解決策はデータが要求された順序で到着することを自動的に保証します。データが取得されている間、レンダリングが中断されるため、レースコンディションを心配する必要はありません。
トランジション
トランジションは、新しい同時実行を可能にする機能です。このAPIは、UIの更新の相対的な優先順位をReactに伝える方法です。 「トランジション」は、主要な画面間の変更などの比較的低優先度の更新です。キーボード入力や他のユーザーの操作に応じた再描画などの更新は、より緊急と見なされます。
更新を遷移としてマークすると、Reactの実行にいくつかの影響があります。Reactは、より緊急の更新が途中で発生した場合に更新を一時停止するために、同時処理の中断可能なレンダリング機能を使用します。これにより、レンダリングが進行中でもユーザーの入力に対してUIが応答し続け、カクつきやじれた感じを減少させることができます。
トランジションは、さまざまな状況で便利です:アプリのヘッダーでの通知ペインの更新、サイドバーの更新管理、UIの他の補助機能の変更などはすべて良い候補です。また、ユーザーの入力に応じて行われる非同期アクションにも適しており、ユーザーがタイプする際に更新される検索バーのクラシックな例があります。
Reactではこれを正しく行うのが難しいことがあります。慎重にデバウンスしないと、新しい結果を取得することによって引き起こされる更新が、一時的にメインスレッドがキーボード入力を処理するのをブロックするため、認識できるレイテンシを感じることが一般的です。React 18では、これらの更新を低優先度の作業としてマークするために遷移を使用することができます。
その
startTransition()APIは状態更新を遷移としてカプセル化します。
import {startTransition} from "react";
const Component = () => {
const [searchQuery, setSearchQuery] = useState("");
const [searchResults, setSearchResults] = useState({});
/**
* State updates within the transition function are low-priority
*/
startTransition(() => {
setSearchResults({text: "Search Result 1"});
});
};
更新が進行中かどうかを確認したい場合は、プレーンを置き換えてください。
startTransition()と共に
useTransition()フック。これにより、遷移に保留中の作業があるかどうかを示すブール値が得られます。
import {useTransition} from "react";
const Component = () => {
const [searchQuery, setSearchQuery] = useState("");
const [searchResults, setSearchResults] = useState({});
const [isSearching, startSearchResultsTransition] = useTransition();
startSearchResultsTransition(() => {
setSearchResults({text: "Search Result 1"});
});
return (
<div>
<input onChange={setSearchQuery} value={searchQuery} />
<SearchResults results={searchResults} />
{(isSearching && "(Searching...)")}
</div>
);
};
既存のすべての状態更新は、古いコードとの互換性を保つため、通常の緊急更新として扱われます。
繰延べ値
遅延値は、長時間実行される更新中に応答性を維持する別の方法です。値が遅延されるとき、
useDeferredValue()フックでは、Reactは指定された期間その古い値を表示し続けます。
const Component = () => {
const [results, setResults] = useState([]);
const deferredResults = useDeferredResults(results, {timeoutMs: 5000});
return <ResultsGrid results={deferredResults} />;
};
Reactが古い結果を5秒間表示し続けることを許可することで、取得したデータが到着した瞬間に即座にレンダリングする必要を排除し、遅延を回避します。これはReactの状態管理に統合されたデバウンスの一形態です。全体的な作業量を減少させるために、データは実際の状態よりも数秒遅れる場合があります。
より良いバッチ処理
React 18における最終的なパフォーマンス指向の変更は、状態更新のバッチ処理に対する一連の改善で構成されています。Reactは、すでにいくつかの単純な状況で状態更新を組み合わせようとしています。
const Component = () => {
const [query, setQuery] = useState("");
const [queryCount, setQueryCount] = useState("");
/**
* Two state updates, only one re-render
*/
setQuery("demo");
setQueryCount(queryCount + 1);
};
しかし、これが機能しないいくつかの状況があります。React 18から、バッチ処理はすべての更新に適用され、どこから来るかに関係なくなります。タイムアウト、プロミス、ブラウザのイベントハンドラーから発生する更新は、コンポーネント内のコードと同じ方法で完全にバッチ化されます。
この変更により、いくつかのコードの動作が変わる可能性があります。上記の場所で状態を複数回更新し、その途中で値をチェックする古いコンポーネントがある場合、React 18ではそれらが期待通りでないことに気付くかもしれません。
flushSync手動で状態の更新を強制的にコミットする方法が利用可能であり、バッチ処理をオプトアウトすることができます。
const Component = () => {
const [query, setQuery] = useState("");
const [queryCount, setQueryCount] = useState("");
const handleSearch = query => {
fetch(query).then(() => {
/**
* Force commit and update the DOM
*/
flushSync(() => setQuery(query));
setQueryCount(1);
});
}
};
サーバーサイドレンダリングの変更
サーバーサイドレンダリングは大幅に改訂されました。新しい機能の目玉は、ストリーミングレンダリングのサポートで、サーバーからReactクライアントへ新しいHTMLをストリーミングできるようになります。これにより、サーバーサイドでSuspenseコンポーネントを使用できるようになります。
この変更の結果、いくつかのAPIが非推奨または再構築されました。その中には
renderToNodeStream()今は使用すべきです
renderToPipeableStream()または
renderToReadableStream()現代のストリーミング環境に対応したサーバーサイドコンテンツを提供するためです。
サーバーでレンダリングされたコンテンツのクライアント側のハイドレーションも、新しい同時レンダリングAPIに合わせて変更されました。サーバーサイドレンダリングと同時モードを使用している場合は、置き換えてください。
hydrate()と一緒に
hydrateRoot():
// OLD
import {hydrate} from "react-dom";
hydrate(<App />, document.getElementById("root"));
// NEW
import {hydrateRoot} from "react-dom/client";
hydrateRoot(document.getElementById("root"), <App />);
ストリーミングレンダリングの効果により、サーバーレンダリングはさまざまなユースケースにより適したものになります。既存のサーバーサイドReactの実装は、クライアントがインタラクティブになる前にアプリケーション全体を取得して水和する必要があります。ストリームとサスペンスを追加することで、Reactは初回レンダリングに必要な部分のみを取得し、その後アプリがインタラクティブになった後にサーバーから追加の非必須データをロードできます。
結論
React 18は、アプリケーションの新しい機能とパフォーマンスの向上を提供します。サスペンスやトランジションなどの機能により、さまざまな種類のコードを書くのが容易になり、アプリの他の部分への影響も少なくなります。
React 18がリリースされた際のアップグレードは、ほとんどの場合、比較的スムーズであるはずです。移行する前に、当面の間はReact 17のルートAPIを使用し続けることができます。
createRoot()同時レンダリングを採用する準備ができたら。今日アプリの準備を始めたい場合は、次のコマンドを実行して最新のリリース候補をインストールできます。
npm install react@rc react-dom@rc