417文字
2分
編集

useEffectEventについてのメモ

イベントとエフェクタの分離が useEffectEvent の目的、useEvent の後継として提案されている。

2024 年 1 月時点では、experimental、canary にも入っていない。

https://react.dev/learn/separating-events-from-effects#declaring-an-effect-event

次のようなカスタムフックを考える。

jsx
function useChatRoom({ roomId, theme }) {
  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.on("connected", () => {
      showNotification("Connected!", theme);
    });
    connection.connect();
    return () => {
      connection.disconnect();
    };
  }, [roomId, theme]);
}

この例では、theme が切り替わる度に ChatRoom への接続が切れてしまう。ただし theme はリアクティブな値であるため、依存関係から入れる必要がある。

useEffectEvent を使えばリアクティブにしたくないイベントを、エフェクトから分離することが出来る。roomId の変更によってのみ再接続が行われる。

jsx
function useChatRoom({ roomId, theme }) {
  const onConnected = useEffectEvent(() => {
    showNotification("Connected!", theme);
  });

  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.on("connected", () => {
      onConnected();
    });
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]);
}

#制約

  • エフェクト内からのみ呼び出せる。
  • 他のコンポーネントやフックには渡せない。
jsx
const onTick = useEffectEvent(() => {
  callback();
});

// これはダメ
useTimer(onTick)

<Timer
  // これはダメ
  onTick={onTick}
/>

#useEffectEvent を利用した例

Callback が変更されても、interval がリセットされない。useEffectEvent を利用しない場合、callback が変更された際に interval がリセットされる。

jsx
function useTimer(callback, delay) {
  const onTick = useEffectEvent(() => {
    callback();
  });

  useEffect(() => {
    const id = setInterval(() => {
      onTick();
    }, delay);
    return () => {
      clearInterval(id);
    };
  }, [delay]);
}

URL が変更されたときのみログを出せる。useEffectEvent を利用しない場合、items が変更された場合にもログが出てしまう。

jsx
function Page({ url }) {
  const { items } = useContext(ShoppingCartContext);
  const numberOfItems = items.length;

  const onVisit = useEffectEvent((visitedUrl) => {
    logVisit(visitedUrl, numberOfItems);
  });

  useEffect(() => {
    onVisit(url);
  }, [url]);
}
編集