stateがオブジェクトの時の更新時の注意点とリセット方法について

### stateがオブジェクトの時の更新時の注意点とリセット方法について
stateがオブジェクトの時の更新時の注意点が少しわかりずらかったのでまとめてみた。

完成画像
![スクリーンショット 2023-01-28 12.21.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/829500/9d2a8d97-b85e-d8f2-28e3-5060e6db1a16.png)


完成コード
Form.jsx（state更新用関数のchangeName,changeAgeなどにsetObjが①②と２つずつあるがどちらを使用しても良い。）

```
import { useState } from "react";


const Form = () => {
 const personObj = { name: "John", age: 20, job: "student" };


 const [obj,setObj] = useState(personObj);


 // 名前、年齢、職業を入力するたびに変更する
 const changeName = (e) => {
   setObj(prev => ({ ...prev, name: e.target.value })) /// ①
   setObj(() => ({ name: e.target.value, age: obj.age, job: obj.job }))  /// ②
 }
 const changeAge = (e) => {
   setObj(prev => ({ ...prev, age: e.target.value }))
   setObj(() => ({ name: obj.name, age: e.target.value, job: obj.job  }))
 }
 const changeJob = (e) => {
   setObj(prev => ({ ...prev, job: e.target.value }))
   setObj(() => ({ name: obj.name, age: obj.age, job: e.target.value }))
 }


 // 入力フォームの値のリセット
 const valueReset = () => {
   setObj({ name: "", age: "", job: "" })
 }


 return (
   <>
     <h3>Name:{obj.name}</h3>
     <h3>Age:{obj.age}</h3>
     <h3>Job:{obj.job}</h3>
     <input id="form1" onChange={changeName} value={obj.name} />
     <input id="form2" onChange={changeAge} value={obj.age} type='number' />
     <input id="form3" onChange={changeJob} value={obj.job} />
     <div><button onClick={valueReset}>リセット</button></div>
   </>
 )
};


export default Form;

```



参考資料
https://amateur-engineer.com/react-usestate-object-update/

オブジェクトの更新方法は、changeName関数の中で記載した①②といった感じで2種類あるらしい。

①「…prev」でオブジェクトにある全てのプロパティの値をstate更新用関数のsetObjに渡し、変更するプロパティの箇所だけ記述する方法(下記の場合、nameプロパティの値を変更するということ)

```
 setObj(prev => ({ ...prev, name: e.target.value }))
```

②オブジェクトの各プロパティを呼び出し、変更する箇所だけ更新する方法(下記の場合、nameプロパティの値を変更するということ)

```
setObj(() => ({ name: e.target.value, age: obj.age, job: obj.job }))
```

②だと、更新したいオブジェクトのプロパティ以外もage: obj.ageのように記述しないと、入力したとき（setObj関数でstateを更新したとき）に値が設定されていないため、その値が表示されなくなる。


ここからは、jobプロパティの中身を更新したとき、setObj関数の中にageプロパティを指定しなかった時の挙動を確認した。

```
const changeJob = (e) => {
   setObj(() => ({ name: obj.name, age: obj.age, job: e.target.value }))
 }
```

↓こちらに変更

```
const changeJob = (e) => {
   setObj(() => ({ name: obj.name, job: e.target.value }))
 }
・・・jobプロパティを更新するときにageプロパティを指定しなかった。
```

jobプロパティを更新する入力フォーム（上から３つ目）に「Fighter」という値を入力すると以下のようになる。

入力前
![スクリーンショット 2023-01-28 11.51.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/829500/efdd31cd-59d6-9f21-767a-6c8756fac05b.png)



入力後
![スクリーンショット 2023-01-28 11.51.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/829500/e54b96b4-7132-5421-223a-c487fcb3d3d9.png)


setObjのstate更新用関数の中にageプロパティを設定していないため、ageプロパティの値が破棄されてしまった。
このようにならないように、ageプロパティの値を維持しておくためには、②のようにオブジェクトのプロパティを全て書くか、①のようにオブジェクトをスプレッド構文で展開し全てのプロパティを取得するかしなければいけない。

また、リセット時も更新時と同じようにしなければいけないらしい。

リセットボタン押下時に実行される関数にageプロパティの記述を削除してみた。

```
// 入力フォームの値のリセット
 const valueReset = () => {
   setObj({ name: "", job: "" })
 }
```

リセットボタン押下前
![スクリーンショット 2023-01-28 11.51.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/829500/9e24c991-8af5-e128-6ffb-d446d4f828b9.png)




リセットボタン押下後
![スクリーンショット 2023-01-28 12.00.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/829500/01affa3a-9911-3747-11f5-be26b1efddd4.png)


上記のようにvalueReset関数にageプロパティを除いたために、ageプロパティの値が削除されず、値が残り表示されたままとなった。
リセットボタンなどで入力フォームの値を全て削除したいときは、下記のようにオブジェクトのプロパティを全て記述しなければいけない。

```
// 入力フォームの値のリセット
 const valueReset = () => {
   setObj({ name: "", age: "", job: "" })
 }
```

### まとめ
stateのオブジェクトの値を更新するときは、以下のようにするのが良いのかもしれない。

```
setObj(prev => ({ ...prev, name: e.target.value }))
```

…prevでスプレッド構文を使用し、オブジェクトの中身を全て取得し、更新するプロパティだけ更新する。