useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)
説明
useSyncExternalStore(
1. subscribe関数,
2. クライアントコンポーネント時のsnapshot関数,
3. SSR時のsnapshot関数
)
getServerSnapshot() 関数を実行して終了。
1. コンポーネントがレンダリングされる
2. useSyncExternalStore が呼ばれる
3. getSnapshot() を呼び出して現在の値を取得
4. その値をコンポーネントに返す
5. コンポーネントのレンダリング完了
6. useEffect相当のタイミングで subscribe(callback) を呼ぶ
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>
);
};