封裝React Hook函式useState實現更優雅的setValue

shellingfordly發表於2022-03-03

react hooks確實很好用,程式碼相比class元件也會簡潔一些,但有時候也會覺得對useState的資料更新時有不太方便的地方,比如宣告陣列或者物件的時候,在設定的時候就得傳一個函式回去,並且使用擴充套件運算子合並物件

setValue((oldValue) => ({
  ...oldValue,
  ...newValue,
}));

感覺在元件裡這樣看著不是太美觀,並且如果元件程式碼多一點的時候,更新一下資料需要佔用三行,尤其在if/for或者回撥裡面使用的時候看著更是難受,於是決定對useState做一下簡單的封裝,讓元件裡更新資料時更優雅一些。

首先寫幾個工具函式isArray、isObject、isNotObject

function isArray(value) {
  return value instanceof Array;
}

function isObject(value) {
  return value instanceof Object && !(value instanceof Array);
}

function isNotObject(value) {
  return typeof value !== "object";
}

然後在我們的自定義hook函式useSetState中,用原聲的useState宣告變數,變數可以直接返回,只需對_setValue做一些操作返回一個新的setValue。

在setValue中,如果initValue為陣列,那新的setValue在傳入單個值時進行push,傳入陣列時進行合併;如果initValue為物件,傳入物件合併,傳入其他型別則丟擲錯誤。

import { useState } from "react";

export default function useSetState(initValue) {
  const [_value, _setValue] = useState(initValue);

  function setValue(newValue) {
    // 初始資料為 陣列
    if (isArray(initValue)) {
      if (isArray(newValue)) {
        _setValue((oldValue) => [...oldValue, ...newValue]);
      } else {
        _setValue((oldValue) => [...oldValue, newValue]);
      }
    }
    // 初始資料為 物件
    else if (isObject(initValue)) {
      if (isObject(newValue)) {
        _setValue((oldValue) => ({
          ...oldValue,
          ...newValue,
        }));
      } else {
        throw new Error(`${JSON.stringify(newValue)} 與初始資料型別不符!`);
      }
    } else if (isNotObject(initValue)) {
      _setValue(newValue);
    }
  }
  return [_value, setValue];
}

實際使用效果

const [obj, setObj] = useSetState({
    a: 1,
    b: 2,
  });

const [arr, setArr] = useSetState([{ id: 1 }, { id: 2 }]);

setObj({ c: 3 }); // {a: 1, b: 2, c: 3}
setArr({ id: 3 }); // [{ id: 1 }, { id: 2 },{ id: 3 }]

相關文章