フロントエンド開発といえば。
react アプリの初期化( npm init vite@latest <アプリ名> )

何か外部の値(例えばwindow size)を取得する、useSyncExternalStore

● useSyncExternalStore の使い方

useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)

説明

useSyncExternalStore(
    1. subscribe関数, 
    2. クライアントコンポーネント時のsnapshot関数, 
    3. SSR時のsnapshot関数
)

● SSR時

getServerSnapshot() 関数を実行して終了。

● クライアントコンポーネント時

1. マウント時

1. コンポーネントがレンダリングされる
2. useSyncExternalStore が呼ばれる
3. getSnapshot() を呼び出して現在の値を取得
4. その値をコンポーネントに返す
5. コンポーネントのレンダリング完了
6. useEffect相当のタイミングで subscribe(callback) を呼ぶ

2. 外部ストアが変更された時

1. resize イベント発生
2. subscribe で登録した callback が呼ばれる 
3. 値に変更がある場合は、コンポーネントを再レンダリング 

src/app/useWindowSize.ts

import { useSyncExternalStore, useRef, useCallback } from 'react';

type WindowSize = {
  width: number;
  height: number;
};

// サーバー側のデフォルト値
const initialValue: WindowSize = {
  width: 0,
  height: 0,
};

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

// SSR時に使用されるスナップショット
const getServerSnapshot = (): WindowSize => {
  return initialValue;
};

export const useWindowSize = (): WindowSize => {
  // useRefでキャッシュを保持(コンポーネントインスタンスごとに独立)
  const cacheRef = useRef<WindowSize>(initialValue);

  const getSnapshot = useCallback((): WindowSize => {
    const width = window.innerWidth;
    const height = window.innerHeight;

    // キャッシュがあり、値が変わっていなければ同じ参照を返す
    if (
      cacheRef.current.width === width &&
      cacheRef.current.height === height
    ) {
      return cacheRef.current;
    }

    // 値が変わった場合のみ新しいオブジェクトを作成する
    cacheRef.current = { width, height };

    return cacheRef.current;
  }, []);

  return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
};

クライアントコンポーネント例

'use client';

import {useWindowSize} from "@/app/useWindowSize";

export const ClientComponent = () => {
  const { width, height } = useWindowSize();
  console.log( 'Window size → ',[width,height] );

  return (
    <div>
      <p>Width: {width}px</p>
      <p>Height: {height}px</p>
    </div>
  );

};

参考リンク: https://jser.dev/2023-08-02-usesyncexternalstore/

No.2714
02/02 13:42

edit