🏃

失敗から学んだ、更新後のStateに依存するsetStateの更新方法

2022/12/18に公開

zenn記事、初投稿

エンジニア歴1年半程度の駆け出しのエンジニアです。
業務では Rails を使用しています。
プライベートで Next.js × Prisma を触っています。
今回、初めてzenn記事を投稿しました。
誤りやより良い方法があれば、ぜひ教えて下さい。

私と同じくこれから学ばれる方へ

ドキュメントに記載されている基礎的な話なのかな? と思いましたので、
これから Next(React) に触れられる方の参考になれば幸いです。

イテレーションで State が意図したように更新できず

複数のレコードの登録後、画面遷移を行わずに登録された情報をStateに追加したかったのですが、
イテレーションで setState による更新を行ったところ、更新後の State を前提にした追加ができず、
複数の登録情報を追加した State に更新できませんでした。

今回の失敗内容は例として後述していますが、先に結論から記載します。

解決方法はドキュメントにあった...

オブジェクトではなく関数を渡す

[state, setState] = useState(obj);
+ setState(currentState => { return currentState.add(newState) });
- setState(state.add(newState);

https://ja.reactjs.org/docs/faq-state.html#how-do-i-update-state-with-values-that-depend-on-the-current-state

ドキュメントにも書いていましたね。

setState へオブジェクトを渡す代わりに関数を渡してください。その関数は常に最新の状態の state を使って呼ばれることが保証されています(次項参照)。

記事を書き始めるまで、ドキュメントに書いてあることに気づけずにいたので、結果的に記事を書こうとしたことで学びが深まったなと思いました。

結論が出たので、ここまでの話ですが、せっかく書き始めたので、失敗例を記載して終わろうと思います。

失敗例

map() 内で setState しているため、setState 後の値が次のsetState時に保証されずに、更新処理が意図通りに行われませんでした。

// 日程別に予約があるとします
const [schedules, setSchedules] = useState([{id: 1, reservations: []}, ...more]);

// 予約を登録すると登録された情報が戻ります
const createReservation = async (data) => {
  const res = await fetch ('/example.com', {
   method: 'POST',
   body: JSON.stringify(data)
  })
  
  return res.json();
}

...

// 何か複数の予約情報の登録があります。
const createRecords = [
  {scheduleId: 1, name: 'taro'},
  {scheduleId: 2, name: 'hanako'},
   ...more
]

// 登録した情報は随時 schedules に追加したい
createRecords.forEach( async data => {
  const reservation = await createReservation(data);
  
  const addReservation = (schedule) => {
    if (schedule.id === reservation.scheduleId) {
      return {
        ...schedule,
	reservations: [...schedule.reservations, reservation]
      }
    } else {
      return schedule
    }
  });
  
  // forEach()内で更新後のschedulesが保証されず意図した状態に更新できなかった
  setSchedules(schedules.map(addReservation));
})

今回の学びから、最終行を以下のように変更しました。

+ setSchedules(currentSchedules => currentSchedules.map(addReservation));
- setSchedules(schedules.map(addReservation));

まだまだ学び始めたばかりですが、これからも自身が躓いたことや気づいたことを発信していきたいと思います。
最後まで読んで頂きありがとうございました。

参考にした記事

今回、私の問題を解決するきっかけとなった方の記事です。
ありがとうございました。

Discussion