logoahooks dive
State

useSet

用于管理 Set 类型状态的 Hook

用法

管理 Set 类型状态。

[
  "Hello"
]

源码

useSet.ts
import { useState } from 'react';
import useMemoizedFn from '../useMemoizedFn';

function useSet<K>(initialValue?: Iterable<K>) {
  const getInitValue = () => new Set(initialValue);
  const [set, setSet] = useState<Set<K>>(getInitValue);

  const updateSet = (updater: (set: Set<K>) => Set<K>) => {
    setSet((prevSet) => updater(new Set(prevSet)));
  };

  const add = (key: K) => {
    if (set.has(key)) {
      return;
    }
    updateSet((newSet) => {
      newSet.add(key);
      return newSet;
    });
  };

  const remove = (key: K) => {
    if (!set.has(key)) {
      return;
    }
    updateSet((newSet) => {
      newSet.delete(key);
      return newSet;
    });
  };

  const reset = () => setSet(getInitValue());

  return [
    set,
    {
      add: useMemoizedFn(add),
      remove: useMemoizedFn(remove),
      reset: useMemoizedFn(reset),
    },
  ] as const;
}

export default useSet;

关于 useMemoizedFn,可以查看对应文档:useMemoizedFn

解读

在不了解 useSet 之前,如果使用 useState 初始化一个 Set 类型的值,更新时需要怎么做呢?

基于 React 的渲染逻辑,每次更新都需要传入一个新的 Set 对象,才能保证页面可以更新。

也即:

const [set, setSet] = useState(new Set())

// add
setSet(prev => {
  prev.add('foo')
  return new Set(prev) // 返回一个新的 Set 对象
})

// remove
setSet(prev => {
  prev.delete('foo')
  return new Set(prev) // 返回一个新的 Set 对象
})

useSet 内部也是这样实现的,只不过为了调用方便,内部将 addremovereset 方法进行了封装。

在内部会定义一个 getInitValue 函数,函数的执行结果返回当前初始值,reset 方法内部就是调用 getInitValue 函数,对 Set 对象进行重置。

function useSet<K>(initialValue?: Iterable<K>) {
  const getInitValue = () => new Set(initialValue)
  const [set, setSet] = useState<Set<K>>(getInitValue)

  /* ... */

  const reset = () => setSet(getInitValue())

  /* ... */
}

Last updated on

On this page