O hirunewani blog

useEffectEventについてのメモ

Created at

useEventの後継として提案されているuseEffectEventについてのメモ。

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

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

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

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

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の変更によってのみ再接続が行われる。

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]);
}

制約

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

// これはダメ
useTimer(onTick)

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

useEffectEventを利用した例

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

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

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

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

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

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

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