はじめに
2022年3月にリリースされたReact 18で追加された新しいフックをまとめてみました。useTransition、useDeferredValue、useSyncExternalStoreなどといったちょっと聞きなれない名前のフックがいくつか追加されたんですよね。
しかし、これらの新しいフックを使いこなせば、Reactアプリケーションのパフォーマンスや開発体験がグッと向上しちゃうとの噂。
現場で18を使っているもののuseIdぐらいしか使ったことがなかったので、ここでそれぞれのフックをざっくりまとめたいと思ったのがきっかけです。
では見ていきましょう!
useId
useIdっていうのは、ユニークなIDを生成するためのフックなんですよ。アクセシビリティAPIを使ったコンポーネントライブラリなんかで、すごく役立つんですよね。React 18のストリーミング対応のサーバレンダラだと、HTMLの順番が保証されないんで、useIdを使ってIDの一意性を確保するのが大事になってくるんです。
function MyComponent() { const id = useId(); return <label htmlFor={id}>...</label>; }
ってな感じで、useIdで生成したIDを使えば、クライアントとサーバで一意なIDが保証されるわけです。
ただ一意の作成方法には他にもやり方があって、個人的には36桁のuuidを生成できるcrypto.randomUUID()
を推してます。
注意点:
useIdはリストのkeyを作るためのものじゃないんで、mapでkeyに渡すのはデータからにしてください〜
useTransition
useTransitionとstartTransitionは、更新の優先度を設定するためのフックなんです。緊急性の高い更新と低い更新を区別できるようになるんですよ。
function App() { const [isPending, startTransition] = useTransition(); function handleClick() { startTransition(() => { // 緊急性の低い更新 }); } return ( <div> {isPending && <Spinner />} <button onClick={handleClick}>Update</button> </div> ); }
こんな感じで、startTransitionの中に緊急性の低い更新を書くと、緊急性の高い更新(テキスト入力の更新とか)が中断されなくなるんです。こちらもUX向上につながる機能っすね。
useDeferredValue
useDeferredValueは、更新の再レンダリングを遅延させるためのフックです。
function App() { const [text, setText] = useState(''); const deferredText = useDeferredValue(text); return ( <> <input value={text} onChange={handleChange} /> <ExpensiveComponent text={deferredText} /> </> ); }
こんな感じで、useDeferredValueで遅延させた値を使えば、再レンダリングが遅延されるんです。しかも、遅延時間が固定じゃないんで、最初のレンダリングが終わったらすぐに遅延レンダリングが始まるんですよね。そのうえ、遅延レンダリングは中断可能なんで、ユーザーインプットをブロックしないっていうメリットもあるらしいです。
useSyncExternalStore
useSyncExternalStoreっていうのは、外部ストアを同期的に更新するためのフックなんですよ。これを使えば、useEffectを使わなくても外部のデータソースを購読できるようになるんです。React外部の状態を扱うライブラリにはぜひ使ってほしいフックっすね。
function Component() { const state = useSyncExternalStore(subscribe, getSnapshot); // ... }
こんな感じで、subscribeとgetSnapshotを渡せば、外部ストアの状態が同期的に更新されるようになるんです。
useSyncExternalStoreはアプリケーションコードじゃなくて、ライブラリで使うことを想定してるフックなんで、そこだけ注意が必要かなーって感じです。
useInsertionEffect
useInsertionEffectは、CSS-in-JSライブラリのパフォーマンス問題を解決するためのフックなんですよ。DOMが更新された後、レイアウト副作用が実行される前に呼ばれるんです。
function MyComponent() { useInsertionEffect(() => { // スタイルの挿入など }); // ... }
こんな感じで、useInsertionEffectの中でスタイルを挿入すれば、レイアウトのずれを防げるんです。React 18の並行レンダリングだと、この問題がより顕著になるんで、CSS-in-JSライブラリを作ってる人には特に重要なフックだと思います。
useInsertionEffectを使う機会はそんなに多くないかもしれないっすね。CSS-in-JSライブラリを自作してるわけじゃなければ、あんまり出番はないんじゃないかなーって気がします。
まとめ
useSyncExternalStoreは外部ストアを同期的に更新できるようになるんで、ライブラリ開発者には嬉しいフックだと思います。useInsertionEffectはCSS-in-JSライブラリのパフォーマンス問題を解決するためのフックで、並行レンダリングとの関係も深いっすね。
まー正直、この2つのフックを直接使う機会はそんなに多くないかもしれませんが、React 18の新機能として知っておくと、Reactの仕組みをより深く理解できるんじゃないかなーって思います。
(とはいえ、まだまだ理解できていない部分も多いので勉強中✍️)
ってことで、React 18で追加された新しいフックについて、ざっくりとまとめてみました!useId、useTransition、useDeferredValue、useSyncExternalStore、useInsertionEffectの5つのフックを使いこなせば、Reactアプリケーションの開発体験がより豊かになるはず。