React Hooksでobject(オブジェクト)を使うCustom Hooks(カスタムフック)


HooksのuseStateでobject(オブジェクト)を使うのが面倒だったので、
useObjectというCustom Hooks(カスタムフック)を作ってみました。

import { useState } from "react";

export default function useObject<T>(obj: T) {
  const [state, setObj] = useState({ ...obj });

  const setState = (next: Partial<T>) => {
    setObj(prev => {
      return { ...prev, ...next };
    });
  };

  const prevState = () => {
    let result: any;
    setObj(prev => {
      result = prev;
      return prev;
    });
    return result as T;
  };

  return { state, setState, prevState };
}

使い方は

interface State {
  name: string;
  pass: string;
}
const initialState: State = { name: "", pass: "", num: 0 };

const App = () => {
    const { state, setState, prevState } = useObject(initialState);

    //通常のstate更新
    setState({name:"something"})

    //事前状態を利用するstate更新
    useEffect(() => {
        setInterval(() => {
          setState({ num: prevState().num + 1 });
        }, 1000);
    }, []);
}

stateがオブジェクトで、setStateでstateを更新できます。 
stateのプロパティでstateを更新する場合はprevState()で事前状態を取得します。 

Hooksによってクラスじゃなくて関数になったのでstate,setStateからthisがなくなった感じになってます。
これで従来のクラスでのsetStateをReact Hooksで使えるようになったはずです...
事前状態を取得するあたりがなんか泥臭いのでいい方法があったら教えてください!

追記
prevState()はレンダリングの回数が増えるので別の方法でも書いてみましたが、事前状態を利用する場合、型の推論が効かないので入力補完が出来ない問題があります。

import { useState } from "react";

export default function useObject<T>(obj: T) {
  const [state, setObj] = useState({ ...obj });

  type func = (prev: T) => Partial<T>;
  type obj = Partial<T>;

  const setState = <U extends func | obj>(next: U) => {
    setObj(prev => {
      if (typeof JSON.stringify(next) === "string") {
        return { ...prev, ...next };
      } else {
        return { ...prev, ...(next as func)(prev) };
      }
    });
  };

  return { state, setState };
}

使い方は


    setState(prev => {
        return { num: prev.num + 1 };
    });