返回

用优雅的姿态走进 State 管理的世界 —— 详解 ahooks State 篇(二)

前端

引言

在前端开发中,状态管理是一个绕不开的话题。ahooks 作为一款优秀的 React Hooks 库,自然也提供了丰富的 State 管理工具。在本文中,我们将对 useMap、useSet、usePrevious、useRafState、useSafeState、useGetState、useResetState 这 7 个 Hook 的源码进行逐一解析,帮助你深刻理解其工作原理和使用方法。

正文

一、useMap

1. 简介

useMap Hook 可以让你轻松地将对象转换为 Map 结构,并自动处理 Map 的变化。它接受一个对象作为参数,返回一个 Map 对象和一个更新 Map 的函数。

2. 源码解析

export function useMap(initialValue) {
  const mapRef = useRef(new Map(Object.entries(initialValue)));
  const [, forceUpdate] = useState({});

  const updateMap = useCallback((updater) => {
    mapRef.current = updater(mapRef.current);
    forceUpdate({});
  }, []);

  return [mapRef.current, updateMap];
}

useMap Hook 的实现原理很简单,它首先创建一个 useRef 引用,用于存储 Map 对象。然后使用 useState Hook 强制组件重新渲染,以便在 Map 发生变化时更新 UI。

updateMap 函数接受一个更新函数作为参数,该函数可以对 Map 进行修改。例如,你可以使用以下代码向 Map 中添加一个新的键值对:

updateMap((map) => {
  map.set('newKey', 'newValue');
  return map;
});

二、useSet

1. 简介

useSet Hook 与 useMap 类似,但它可以让你轻松地将对象转换为 Set 结构,并自动处理 Set 的变化。它接受一个数组作为参数,返回一个 Set 对象和一个更新 Set 的函数。

2. 源码解析

export function useSet(initialValue) {
  const setRef = useRef(new Set(initialValue));
  const [, forceUpdate] = useState({});

  const updateSet = useCallback((updater) => {
    setRef.current = updater(setRef.current);
    forceUpdate({});
  }, []);

  return [setRef.current, updateSet];
}

useSet Hook 的实现原理与 useMap 类似,它首先创建一个 useRef 引用,用于存储 Set 对象。然后使用 useState Hook 强制组件重新渲染,以便在 Set 发生变化时更新 UI。

updateSet 函数接受一个更新函数作为参数,该函数可以对 Set 进行修改。例如,你可以使用以下代码向 Set 中添加一个新的值:

updateSet((set) => {
  set.add('newValue');
  return set;
});

三、usePrevious

1. 简介

usePrevious Hook 可以让你获取组件上一次渲染时的某个状态值。它接受一个值作为参数,返回该值的上次值。

2. 源码解析

export function usePrevious(value) {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  }, [value]);
  return ref.current;
}

usePrevious Hook 的实现原理很简单,它使用 useRef Hook 来存储组件上一次渲染时的值。然后在每次组件更新时,它都会将当前值赋给 useRef 引用。这样,你就可以在组件的任何地方获取到上一次渲染时的值。

例如,你可以使用以下代码获取组件上一次渲染时的计数器值:

const Counter = () => {
  const [count, setCount] = useState(0);
  const previousCount = usePrevious(count);

  return (
    <div>
      <h1>{count}</h1>
      <button onClick={() => setCount(count + 1)}>+</button>
      <p>Previous count: {previousCount}</p>
    </div>
  );
};

四、useRafState

1. 简介

useRafState Hook 可以让你在浏览器下一次重绘时更新状态。它接受一个更新函数作为参数,该函数可以对状态进行修改。

2. 源码解析

export function useRafState(initialState) {
  const rafRef = useRef();
  const [state, setState] = useState(initialState);

  const setRafState = useCallback((updater) => {
    cancelAnimationFrame(rafRef.current);
    rafRef.current = requestAnimationFrame(() => {
      setState(updater(state));
    });
  }, [state]);

  useEffect(() => {
    return () => cancelAnimationFrame(rafRef.current);
  }, []);

  return [state, setRafState];
}

useRafState Hook 的实现原理是利用浏览器