Reactでオブジェクトの場合はuseStateよりuseImmerのほうが便利

useImmerとは

useImmerはReactのフックであり、Immerライブラリを使用して、状態の不変性を簡単に管理できます。

Reactの状態管理において、オブジェクトのネストされたプロパティを更新するのは煩雑になりがちです。

しかし、Immerを使うことで、変更可能な操作のように見えるコードを書いても、実際には不変の状態が保たれます。

例えば以下のようなオブジェクトの場合、値を変更する際にスプレッド構文(...person)を多様することになり、コードの可読性が悪くなります。

TSX
import { useState, ChangeEvent } from 'react'

export default function App() {
  const [person, setPerson] = useState({
    name: 'Yamada',
    work: {
      company: 'NTT',
    }
  })

  function handleNameChange(e: ChangeEvent<HTMLInputElement>) {
    setPerson({
      ...person,
      name: e.target.value,
    })
  }

  function handleCompanyChange(e: ChangeEvent<HTMLInputElement>) {
    setPerson({
      ...person,
      work: {
        ...person.work,
        company: e.target.value,
      }
    })
  }

  return (
    <>
      <label>
        Name:
        <input
          value={person.name}
          onChange={handleNameChange}
        />
      </label>
      <br />
      <label>
        Company:
        <input
          value={person.work.company}
          onChange={handleCompanyChange}
        />
      </label>
      <hr />
      <p>
        {person.name} works for {person.work.company}
      </p>
    </>
  )
}

React Object useState Sample

useImmerを使った場合

useStateの代わりにuseImmerを使うと、スプレッド構文(…person)を使わないシンプルなコードになります。

まず、useImmerを以下のコマンドでインストールします。

ShellScript
npm i -D use-immer

次にuseImmerをimportして、useStateをuseImmerに書き換えます。

TSX
import { ChangeEvent } from 'react'
import { useImmer } from 'use-immer'

export default function App() {
  const [person, setPerson] = useImmer({
    name: 'Yamada',
    work: {
      company: 'NTT',
    }
  })
  // 中略
}

最後にhandleNameChangeなどのChangeEventをスプレッド構文を使わない以下の形に変更します。

TSX
import { ChangeEvent } from 'react'
import { useImmer } from 'use-immer'

export default function App() {
  const [person, setPerson] = useImmer({
    name: 'Yamada',
    work: {
      company: 'NTT',
    }
  })

  function handleNameChange(e: ChangeEvent<HTMLInputElement>) {
    setPerson(draft => {
      draft.name = e.target.value
    })
  }

  function handleCompanyChange(e: ChangeEvent<HTMLInputElement>) {
    setPerson(draft => {
      draft.work.company = e.target.value
    })
  }

  return (
    <>
      <label>
        Name:
        <input
          value={person.name}
          onChange={handleNameChange}
        />
      </label>
      <br />
      <label>
        Company:
        <input
          value={person.work.company}
          onChange={handleCompanyChange}
        />
      </label>
      <hr />
      <p>
        {person.name} works for {person.work.company}
      </p>
    </>
  )
}

以上の変更だけで、スプレッド構文を使わないシンプルなコードに変更できます。

React Object useImmer Sample