⛰️

ReactでuseStateの中身を連想配列で持つ場合の更新

2022/02/21に公開3

Reactで連想配列をuseStateに入れて管理したときに、状態が更新されなくて困りました。


例えばこのようなデータを...

data = {
  name: "東京",
  num: 1
}



ひとつのuseStateに入れたら...

const [setting, setSetting] = useState(data)



このようにしても更新されず...💦

const changeName = (e) => {
  setSetting((_setting) => {
    _setting.name = e.target.value
    return _setting
  })
}



更新するには、配列を複製、複製の中身を更新して、戻してやる必要があるみたい

const changeName = (e) => {
  let _copy = JSON.parse(JSON.stringify(setting)) // 複製
  _copy.name = e.target.value // 複製を更新
  setSetting(_copy) // 戻す
}



以下のようなアドバイスをいただいたので追記します。
こちらの方が良いですね。記事にしてよかった!


【追記1】オブジェクト生成による状態更新

const changeSetting = (e: React.ChangeEvent<HTMLInputElement>) => {
  setSetting((preSetting) => ({
    ...preSetting,
    name: e.target.value
  }))
}



【追記2】useImmerによる状態管理

import { useImmer } from "use-immer"
const [data, setData] = useImmer(data_INI);
const changeData = (e: React.ChangeEvent<HTMLInputElement>) => {
  setData((draft) => {
    draft.name = e.target.value;
  })
}



TypeScriptでまとめるとこのように。(CodeSandbox)

Discussion

masakimasaki

Reactはstateの更新をObject.isで評価しているので、当初の書き方ではsetSetting後にコンポーネントの再レンダリングが起きなかったのだと思います。

(コピーを作成してsetStateした場合に更新されるのも上記が理由)

title
setSetting(preSetting => ({
  ...preSetting,
  name : e.target.value
}))

のように、別オブジェクトを生成しnameだけ書き換えてあげるとひと手間減るかもしれません👀

らっくらっく

ありがとうございます。
いいですね!可読性もありますし。
試してみて、記事に追記させていただきました