可监听变化的 atom

一个 atom 内容的变化通常只有使用useAtom获取 atom 的值以后,才可能完成其中值变化的监听,而且功能也非常少。这里提供一个创建可被监听的 atom 的工具函数,其创建的 atom 可以被监听,可以使用回调函数在 atom 的set方法被调用的时候处理响应 atom 值变化的逻辑。

type Callback<Value> = (
  get: Getter,
  set: Setter,
  newVal: Value,
  prevVal: Value
) => void;

export function atomWithListeners<Value>(initialValue: Value) {
  const baseAtom = atom(initialValue);
  const listenersAtom = atom<Callback<Value>[]>([]);
  const anAtom = atom(
    (get) => get(baseAtom),
    (get, set, arg: SetStateAction<Value>) => {
      const prevVal = get(baseAtom);
      set(baseAtom, arg);
      const newVal = get(baseAtom);
      get(listenersAtom).forEach((callback) => {
        callback(get, set, newVal, prevVal);
      });
    }
  );
  const useListener = (callback: Callback<Value>) => {
    const setListeners = useSetAtom(listenersAtom);
    useEffect(() => {
      setListeners((prev) => [...prev, callback]);
      return () =>
        setListeners((prev) => {
          const index = prev.indexOf(callback);
          return [...prev.slice(0, index), ...prev.slice(index + 1)];
        });
    }, [setListeners, callback]);
  };
  return [anAtom, useListener] as const;
}

这个 atom 在创建的时候,除了会返回 atom 本身,还会返回一个 Hook。这个 Hook 可以接受一个类型为(get, set, newValue, prevValue) => void的回调函数,并且在 atom 值发生变化的时候调用这个回调函数。为了回调函数的稳定引用,可以在组件中使用useCallback来定义这个回调函数。