はじめに
React18について学びたいと思いながらも、日々が経ってしまいました。
ただその間にもReact18についてキャッチアップしているかたももちろんいるわけで、変更点やアップデート内容を知っている人と知っていない人の間にかなりの差が出来てしまってしまっている気がします。この記事ではそんな出遅れてしまった私がなんとか追いつけるようにすることを目標に執筆いたしました。
学ぶ必要性について
Reactは定期的にアップデートしているため、別に今回のアップデートもそれの一環でしょって思いがちなのですが、実は今回のアップデートは革命と言われており多くの変更点や今後フロントエンド開発がガラリと変わるような予感のするアップデート、もしくはその準備段階の機能が多々出てきているので必見です。
React18は17などのアップデートに比べてかなり重要なバージョンアップである。
概要
React18は様々なアップデートがありますが、下記の3つを押さえておけばまず間違い無いかなと思います。
当記事では1と2を取り上げます。
- Automatic batching
- Transition
- Suspense
各機能や概要に関して、一つ一つ噛み砕いてアウトプットしていきたいと思います。
Root に関して
前提としてReact18とそれ以前のバージョンではindex.js において、ReactDOM の表示方法が違いcreateRoot を用いて使用しているという違いがあります。
// React18 createRootにすると18の機能が使えるようになる
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
//React17
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementByID("root")
);
Automatic batching
まずAutomatic batchingから説明していきたいと思いますが、その前にバッチングについて詳しくみていきます。
バッチングとは?
バッチングに関して公式ドキュメントにはこのように書いてあります。
バッチングとは React がパフォーマンスのために複数のステート更新をグループ化して、単一の再レンダーにまとめることを指します。(React公式Docsより)
ただこれだけだと分かりづらいですよね... 一言で言うとset 関数などをハンドル内で呼んだとしてもその都度レンダリング走るのではなく、ある程度自動的にまとめてくれることです。
例
const [state1, setState1] = useState(false);
const [state2, setState2] = useState("");
const [state3, setState3] = useState(0);
-----------------中略--------------------
const onClickHandler = () => {
//set関数が3回も呼ばれているがその都度レンダリングが走るわけではなく、まとめて一回のみ走るようにしてくれる(バッチング)
setState1(true);
setState2("OK");
setState3(1000);
};
実はReact18以前にもこの自動バッチング自体は行われていましたが、18で新しく変わった点としてイベントハンドラー以外にもバッチ処理がされるようになったことです。
以前はイベントハンドラ以外はバッチ処理されていなかった
自動バッチング以前は、React のイベントハンドラ内での更新のみバッチ処理されていました。promise や setTimeout、ネイティブのイベントハンドラやその他あらゆるイベント内で起きる更新はデフォルトではバッチ処理されていませんでした。(公式ドキュメントより)
どういうメリットがあるの?
以前は何回も処理が走ってしまっていたりかなり効率の悪いコードになっていた可能性があるが、今回の改訂のおかげでパフォーマンスが向上されました。
ちなみに、このアップデートに関しては特にこちら側が設定することなく勝手に最適化してくれるが一応知っておいた方がいいと思います
Transition について
今回のアップデートで大きく変わった内容にもなるので注視する必要があるのがこの Transition です。
Transitionを解説する前にTransitionがどのような問題を解決するのかをみていきたいと思います。
今までの開発(Transitionが導入される前)
これまでは優先位順位がなく更新がされていました。それによって下記のような障害が生まれていました。
- 押したかどうか不安になるようなボタン
- 感度の悪い、絞り込みのためのタグ
- 数秒経った後に変化のある要素、などなど...
これらUXの悪い例に共通して言えるのが、ユーザーが何らかアクション(クリック,フォーカス等)を行った際のレスポンスの遅さにあります。
また上記の例の原因は重い処理と同時にレンダリング処理がされていることが原因です。
イメージとしてはこんな感じでしょうか?
React「激オモ処理の対処中でお腹いっぱい、ユーザーからのアクションまで手がまわんないよ💦」
React「ようやく処理とアクションの対処が終わったぁ」← これがUXの悪さの原因
今までのReactとしてはどの処理であっても同一視、つまり優先順位をつけることができないのです。
そのため、「この処理から先に手をつけよう、この処理は重いから後に回そう」ということが出来なかったわけです.
ただ、これだとユーザーとしてもUXが悪く、離脱率が上がる原因にもなってしまいます。
上記の内容についてさらに詳しく知りたい方はこのgitHubがおすすめです。
解決策
この問題を解決するために新たに実装された機能が先述したTransition
という概念です。
Transitionって何?
トランジション(transition; 段階的推移)とは React における新たな概念であり、緊急性の高い更新 (urgent update) と高くない更新 (non-urgent update) を区別するためのものです。
Transitionによって新しく生まれた概念、機能であり UX の向上に役立つ機能で、今後は当たり前のようにTransitionを用いて開発が行われるようになると思われます。
一言でまとめるとどういうこと?
今まで均一的な更新だったのが、優先順位をつけて更新できるようになった!ということです。
これによって更新に順序づけられるため、Reactに順番を伝えることができるようになったわけです。
イメージとしてはこんな感じでしょうか
React「激オモ処理とユーザーからのアクション二つあるけど、先にアクションの方を更新しろと言われているからこっちから先に片付けよう」
React「よしっ、アクションをすぐ反映して、激オモ処理に集中しよう」
実際のコードの書き方
import { useTransition } from 'react';
// ...
function ClassicAssociationsBubbles({associations}) {
// New hook in React 18
const [isPending, startTransition] = useTransition();
const [minScore, setMinScore] = useState(0.1);
const data = computeData(minScore);
return (
<>
<FastSlider defaultValue={0.1} onChange={val => {
// Update the results, in a transition.
startTransition(() => {
setMinScore(val);
});
}/>
<ExpensiveChart
// Use the isPending flag to add a pending class.
className={isPending ? 'pending' : 'done'}
data={data}
/>
</>
);
}
//このコードはReal world example: adding startTransition for slow renders #65から引用させていただきました。
上記のコードのようにuseTransitionを用いて書くことができ、startTransitionでラップされたセット関数等は優先順位が下がり処理されます。
実際に触りながら、より詳しく理解したい方は下記参考記事がおすすめです✨
終わりに
React18は今後の開発体験をガラッと変えてしまうかもしれないそんな予感を感じたアップデートだと個人的に感じたため、気を引き締めてキャッチアップしていきたいと思います!
参考資料