【React】1度だけ変更されるstateにはuseStateよりuseReducerを使う方が最適
useStateでしょ! という声が聞こえてきますね。
おっしゃる通りなんですが、それよりも実はそういう時はuseReducerの方がより適しているという話です。
はじめましてスペースマーケットでフロントエンドエンジニア兼リーダーをしています。
はじめに
useState / useReducerがなんぞや?という方は公式のリファレンスをご確認ください。
1度だけ変更されるstateでいうと下記のようなイメージでしょうか?
const Hoge: FC = () => {
const [finished, setFinished] = useState(false)
const onClick = () => {
setFinished(true)
}
if (finished) return <div>送信しました!</div>
return (
<div>
<button onClick={onClick}>送信する</button>
</div>
)
}
例えば問い合わせ画面のような画面があり、送信ボタンを押すと問い合わせの画面が完了画面へ切り替わるようなイメージです。
今回はフォームなどなど諸々を省略していますがご容赦ください。
これをuseReducerで書き直してみます。
const Hoge: FC = () => {
const [finished, updateFinished] = useReducer(() => true, false)
const onClick = () => {
updateFinished()
}
if (finished) return <div>送信しました!</div>
return (
<div>
<button onClick={onClick}>送信する</button>
</div>
)
}
このようになります。
このようにすることで次のメリットが生まれます。
useReducerはstateの再更新をしないことを明示できる
結局これにつきます。
もう一度コードをみてください。
const [finished, setFinished] = useState(false)
本来この画面では「送信するを押したら完了表示になり元の画面へ切り替わらない」ことが正しいです。
しかしfinished
はこの画面の要件を満たせていません。
useStateを使う場合、finishedの値はsetFinished
の実行結果によって更新されます。
今回はかなり小さな構成であり、早々このような事が起きないと思います。
もっと複雑な構成である場合、意図しないタイミングでsetFinished
が実行されることでバグを埋め込む可能性があります。
そのような可能性が生まれないようにしておければそれに越したことはありません。
上記のような場合に使うべきがuseReducerです。
const [finished, updateFinished] = useReducer(() => true, false)
useReducerを使ったupdateFinished
を実行するとfinished
のstateはtrueへ更新されます。
useStateと異なる点は、 updateFinished
を何度実行してもfinished
はtrueにしかなりません。
もう一度falseに戻ることがないので、「このstateは再更新されることはない」ということを明示できます。
これで誤った実装によるバグが生まれる可能性がなくなります。
おわりに
とてもシンプルな話ですが、とりあえず仕様を満たせばいいわけではなくコードでいかに仕様を表現するか大事だと思います。
みなさんのプロダクトでも是非こちら活用してみてください!
スペースを簡単に貸し借りできるサービス「スペースマーケット」のエンジニアによる公式ブログです。 弊社採用技術スタックはこちら -> whatweuse.dev/company/spacemarket
Discussion
良い記事ありがとうございます!
端的に言うと
されないというのが利点ですね?
いままで「とりあえずuseState」でしたが、
「useReduserでメリットがないならuseState」くらいの意識のほうが良さそうですね!
コメントありがとうございます!
おっしゃる通りで、終わったものを再度元に戻すような実装が埋め込むことができないというのがメリットになります
こんな記事を書いておいてなんですが組織の風土やメンバーのレベル感などで変わると思うので一概にはこうすべき!とは言えないのが正直なところです笑
このような記事に反響をいただけたことは、
「言われてみたらこんなのできるじゃん」
「結構いいじゃん!」
「どうなんだろうこれは、いまいちかも」
と色々な意見があった結果だと思いますので、この実装が本当に「いいものか」というと一概にそうは言えないと思います。(正直初見だと???となると思いますので・・・)
一番良い形は、「このコードの書き方が本当にいいのか」「今回の要件はこれでいいんだっけ?」というのをチームで議論しつつ納得した上で実装に落とし込むことだと思います
useState使うのか、useReducerの方がいいんじゃないか、reduxなどのライブラリ使った方がいいのか?と言ったお話のきっかけに使っていただければ嬉しく思います!
Hoge Componentがunmountされるときには、finishedがfalseにリセットされることはありますか?
unmountされるタイミングでfnishedがfalseになることはありません
ただunmountしたあと再度mountした場合は、stateが初期化されるのでfinishedはfalseになります!