Techtouch Developers Blog

テックタッチ株式会社の開発チームによるテックブログです

useSyncExternalStoreを使ってみた

テックタッチアドベントカレンダー17日目を担当する kenshin です。
今年もあと少しで終わりですね。今年を振り返るのために2022年に食べたラーメンの杯数を数えてみると121杯(執筆時点)でした。年末までにあと何杯食べられるかな。

さて、今回は React18 で追加された useSyncExternalStore を使ってみました。

useSyncExternalStore とは

React18 で新たに追加された React フックです。

React コンポーネントの多くは props、state、context からデータを参照します。
ただし、React 外部のデータソースから値を参照する必要がある場合も存在します。
このような場合、 useSyncExternalStore を使うことによって、 React 外部のデータソースのストアを監視し、ストアの値を参照できます。

利用場面

React Docs BETAに、 useSyncExternalStore をどのような場面で利用すればよいかが記載されていました。

①React の外部で状態を管理するライブラリを開発するとき(ライブラリ製作者向け)
②ブラウザーのイベント API をサブスクライブするとき

基本的には useState、useReducer (または状態管理ライブラリ)を使って状態管理すべきで、もし上記ケースに該当する場合は useSyncExternalStore の利用を検討してみてもよいのかもしれません。

使い方

コンポーネントのトップレベルで、 useSyncExternalStore を呼び出します。
useSyncExternalStore の引数には、ストアが変更されるたびに呼び出されるコールバックを登録するための subscribe 関数、ストアの現在の値を返す getSnapshot 関数、そしてオプションで getServerSnapshot を渡します。
getServerSnapshot はサーバーレンダリング時にスナップショットを返すための関数です。

import { useSyncExternalStore } from 'react'
import { subscribe, getSnapshot, getServerSnapshot } from './externalStore.js'

function ExternalStore() {
  const snapshot = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)

  // snapshot: ストアの値
  // subscribe: ストアが変更されるたびに呼び出されるコールバックを登録するための関数
  // getSnapshot: ストアの現在の値を返す関数
  // getServerSnapshot: サーバーレンダリング時にスナップショットを返すための関数

  // ...
}

実際に使ってみよう

今回は利用場面②ブラウザーのイベント API をサブスクライブするケースで、 resize イベントを監視し、ビューポートの幅を取得してみたいと思います。

まず、 resize イベントをサブスクライブする subscribe 関数とビューポートの幅を返す getSnapshot 関数を用意します。
getSnapshot 関数は、 window.innerWidth から取得した値を返します。
subscribe 関数は、引数の callback をイベントリスナーに登録し、登録したイベントリスナーをクリーンアップする関数を返します。

const getSnapshot = () => {
  return window.innerWidth;
};

const subscribe = (callback) => {
  window.addEventListener("resize", callback);
  return () => window.removeEventListener("resize", callback);
};



用意した2つの関数(subscribe、getSnapshot)を useSyncExternalStore の引数に渡すことで、 window.innerWidth の値を読み取り、変更をサブスクライブできます。

const ViewportWidth = () => {
  const snapshot = useSyncExternalStore(subscribe, getSnapshot);
  
  return (
    <div>{snapshot}</div>
  );
}



実際にビューポートの幅を変更してみると、snapshot の値が変わっているのがわかります。

最後に

今回は React18 で追加された useSyncExternalStore を試してみました。
useSyncExternalStore を使うことで、React コンポーネント内で外部の変更をサブスクライブし、値を参照できました。

今回は、ブラウザのイベント API をサブスクライブするところまでしか試せていませんが、今後は useSyncExternalStore を使って、簡易な状態管理ライブラリも実装してみたいです。

最後まで読んでいただきありがとうございました。