useSyncExternalStore
from React Hooks
外部のデータストアからの読み出しや、subscribeするために使う
たぶんv18から入った
docs
code:ts
const state = useSyncExternalStore(
subscribe, // データストアに変更あった際に呼び出すcallback
getSnapshot, // 現在のデータストアの内容を返す
getServerSnapshot // optional. server rendering時にsnapshotを返す
);
useSyncExternalStoreを使いまくる - Speaker Deck
わかりやすい
#WIP
code:ts
const state = useSyncExternalStore(
subscribe, // データストアに変更あった際に呼び出すcallback
getSnapshot, // 現在のデータストアの内容を返す
getServerSnapshot // optional. server rendering時にsnapshotを返す
);
基本的には、getSnapshotの関数の結果を返す
SSR時とHydration時は、getSnapshotの代わりにgetServerSnapshotが実行される
subscribeは、callback => () => voidな関数
もっとわかりやすく書くと、callbackを引数に取り、return cleanup functionする
code:ts
const subscribe = callback => {
// 外部storeに変更があった際にcallbackを呼ぶ
return () => {..}
}
callbackが呼ばれると、getSnapshotが再度呼ばれる
https://speakerdeck.com/ssssota/use-usesyncexternalstore?slide=11
https://gyazo.com/5bb6ebdcbb498bd5b130d35a1335fe9b
with ResizeObserver
https://zenn.dev/osushi02/scraps/6ec122ed44ad30
例 ref
code:ts
export function useOnlineStatus() {
return useSyncExternalStore(
subscribe(),
() => navigator.onLine,
() => true
);
}
function subscribe(callback) {
window.addEventListener('online', callback);
window.addEventListener('offline', callback);
return () => {
window.removeEventListener('online', callback);
window.removeEventListener('offline', callback);
};
}
これほんまに使い方として正しいのか?
「return () => {..}でunsubscribeする」ってdocs読んでも自明な使い方ではない
useRefを使ってref.current.addEventListenerをする時に辛い
useCallbackを使えばいいが、ref.current == nullのときの扱いで型が合わない
interfaceとして求められているものと使い方が合致してない感じがする
この記事って公式なので合ってる気がするが、歪な感じが否めない
こんな
code:ts
const ref = useRef<HTMLDivElement | null>(null);
const subscribe = useCallback((callback: () => void) => {
if (ref.current == null) return () => {};
ref.current.addEventListener('contextmenu', callback);
return () => {
ref.current.removeEventListener('contextmenu', callback);
};
}, []);
useSyncExternalStore(subscribe, () => {});
たんじゅんにこれの使い方がおかしいだけか
そもそも「現在のstoreの値」など存在しない
つまり「常にaddEventLisneterはuseSYncExternalStoreに書くべき」ではない
useEffect内でaddEventListener/removeEventListenerを使ってsubscribeしない
代わりにuseSyncExternalStoreを使う
useEffect内に書くのではなく、このhooksを使う
↑これは言い過ぎ
docsでも普通にuseeffectが使われている
が、こっちのページではuseSyncExternalStoreの使用を推奨していた
https://zenn.dev/kazuma1989/articles/a30ba6e29b5b4c#usesyncexternalstore-版
こういう感じでrefをexternal storeと見なしたらいけるかもしれない
https://ja.react.dev/learn/lifecycle-of-reactive-effects#can-global-or-mutable-values-be-dependencies
代替策として、外部のミュータブルな値の読み取りやリッスンをするには useSyncExternalStore を利用しましょう。
#??
refが必要なとききつない??
参考
You Might Not Need an Effect
/miyamonz/React 18 for External Store Libraries
/tosuke/useSyncExternalStore
https://qiita.com/uhyo/items/6a3b14950c1ef6974024
https://zenn.dev/stin/articles/use-sync-external-store-with-match-media