④react-ahooks原始碼分析之useSet

Non_發表於2022-11-24

useSet

簡介

首先,set是es6新增的資料結構,類似陣列,但set不能有相同的成員,可以利用這點來給陣列去重;在set內新增成員比array快,但set在排序上不如陣列靈活。

useSet是一個用來管理set型別state的hook,封裝了以下幾個功能:

  • add:增加元素
  • remolve:刪除某個元素
  • reset:重置回初始狀態

簡單示例

const [set,{add,remove,reset}
] = useSet<string>(['a','b']);//set為['a','b']

add('c')//set為['a','b','c']

remolve('b')//set為['a','c']

reset()//set為['a','b']

原始碼分析

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

function useSet<K>(initialValue?: Iterable<K>) {

//獲取初始值:使用new Set建立一個以初始傳入引數為內容的set,或者一個空set
  const getInitValue = () => {
    return initialValue === undefined ? new Set<K>() : new Set(initialValue);
  };

//用基本的useState來封裝該hook
  const [set, setSet] = useState<Set<K>>(() => getInitValue());

//add方法:若set已有該值,則不做處理;若無,則使用set的add方法新增元素
  const add = (key: K) => {
    if (set.has(key)) {
      return;
    }
    setSet((prevSet) => {
      const temp = new Set(prevSet);
      temp.add(key);
      return temp;
    });
  };

//remove方法:若set沒有該值,則不做處理;若無,則使用set的delete方法刪除元素
  const remove = (key: K) => {
    if (!set.has(key)) {
      return;
    }
    setSet((prevSet) => {
      const temp = new Set(prevSet);
      temp.delete(key);
      return temp;
    });
  };

//reset方法:重置回初始值
  const reset = () => setSet(getInitValue());

//返回使用了useMemoizedFn這個hook
  return [
    set,
    {
      add: useMemoizedFn(add),
      remove: useMemoizedFn(remove),
      reset: useMemoizedFn(reset),
    },
  ] as const;
}

export default useSet;

要點分析

該hook在return方法的時候使用了useMemoizedFn這個hook。該hook和useCallback類似。

但不同之處在於useCallback返回的是回撥函式的memoized版本,當依賴項不改變的時候,它不會更新函式,但新增依賴項的時候,函式會重新渲染,這就失去了快取函式的意義;而useMemoizedFn解決了這個問題,可以用來快取各種函式,並且函式的地址不會被改變。

基於useSet的改寫:useArray,實現了陣列的push、pop、shift、unshift方法

在使用useSet的過程中,由於useSet的add方法是將元素直接加在set的後面,感覺想要將元素插在set前面不太靈活,於是照著該原始碼,簡單地改編了一個useArray,實現了陣列的push、pop、shift、unshift方法。

程式碼如下:

import { useState } from 'react';
import { useMemoizedFn } from 'ahooks';

function useArray<K>(initialValue?: Iterable<K>) {
  const getInitValue = () => {
    return initialValue === undefined ? new Array<K>() : Array.from(initialValue);
  };

  const [array, setArray] = useState<Array<K>>(() => getInitValue());

  const push = (...key: K[]) => {
    setArray((prevSet) => {
      const temp = Array.from(prevSet);
      temp.push(...key);
      return temp;
    });
  };

  const unshift = (...key: K[]) => {
    setArray((prevSet) => {
      const temp = Array.from(prevSet);
      temp.unshift(...key);
      return temp;
    });
  };

  const pop = () => {
    setArray((prevSet) => {
      const temp = Array.from(prevSet);
      temp.pop();
      return temp;
    });
  };

  const shift = () => {
    setArray((prevSet) => {
      const temp = Array.from(prevSet);
      temp.shift();
      return temp;
    });
  };

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

  return [
    array,
    {
      push: useMemoizedFn(push),
      pop: useMemoizedFn(pop),
      shift: useMemoizedFn(shift),
      unshift: useMemoizedFn(unshift),
      reset: useMemoizedFn(reset),
    },
  ] as const;
}

export default useArray;

簡單使用

const [array,{push,pop,shift,unshift,reset}
] = useArray<string>(['a','b']);//set為['a','b']

push('c','cc')//set為['a','b','c','cc']

pop()//set為['a','b','c']
...

相關文章