Engineering from Scratch

エンジニア目指してます

useImmer

useState と同様に,state と state を更新する関数のタプル型を返す hook。state を更新する関数は,immer の produce 関数か値を引数に取れる。

immer producer function

produce関数は以下のような interface となる。

produce(baseState, recipe: (draftState) => void): nextState

baseStateを,recipe関数によって変更した結果を返す。しかし,baseState は immutable。

import produce from "immer";

const baseState = [
  {
    title: "Learn TypeScript",
    done: true,
  },
  {
    title: "Try Immer",
    done: false,
  },
];

const nextState = produce(baseState, (draftState) => {
  draftState.push({ title: "Tweet about it", done: false });
  draftState[1].done = true;
});

Managing state with immer producer function

import { useImmer } from "use-immer";

export default function App() {
  const [person, updatePerson] = useImmer({
    name: "Michel",
    age: 33,
  });

  function updateName(name: string) {
    updatePerson((draft) => {
      draft.name = name;
    });
  }

  function becomeOlder() {
    updatePerson((draft) => {
      draft.age++;
    });
  }

  return (
    <div className="App">
      <h1>
        Hello {person.name} ({person.age})
      </h1>
      <input
        onChange={(e) => {
          updateName(e.target.value);
        }}
        value={person.name}
      />
      <br />
      <button onClick={becomeOlder}>Older</button>
    </div>
  );
}

updatePersonpersonstate を更新する関数を渡すことで,state が更新される。

Managing state as simple useState hook

import { MouseEvent } from "react";
import { useImmer } from "use-immer";

export default function App() {
  const [age, setAge] = useImmer(20);

  function birthDay(event: MouseEvent<HTMLButtonElement>) {
    setAge(age + 1);
  }

  return (
    <div>
      {age}
      <button onClick={birthDay}>It is my birthday</button>
    </div>
  );
}

useImmer の更新関数(setAge)に値を渡すと,useState と同じ挙動となる。

useImmerReducer

useReducerと同じことができる。

import { useImmerReducer } from "use-immer";

type State = {
  count: number;
};

const initialState: State = { count: 0 };

type Action = { type: "reset" | "increment" | "decrement" };

function reducer(draft: State, action: Action) {
  switch (action.type) {
    case "reset":
      return initialState;
    case "increment":
      return void draft.count++;
    case "decrement":
      return void draft.count--;
  }
}

export default function App() {
  const [state, dispatch] = useImmerReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: "reset" })}>Reset</button>
      <button onClick={() => dispatch({ type: "increment" })}>+</button>
      <button onClick={() => dispatch({ type: "decrement" })}>-</button>
    </>
  );
}
参考

github.com