Reactを勉強し始めたときに必ずと言って良いほど「カウントアップ」を作りますよね。
自分も同じくカウントアップから勉強したのですが、意外と深堀りせずに次のステップに進んでしまっていました。
「useState」の扱いには少しばかり注意が必要で、自分もハマったことがあるので共有です。
また動画も用意しているのでお好みでどうぞ。
意外とやりがちなuseStateのミス
まずネットで調べて辿り着くカウントアップの作り方を紹介します。
初心者向けに公開されている例の大半が以下のように作られています。
import React, { useState } from "react";
const Count = () => {
const [count, setCount] = useState(0);
const handleCount = (num) => {
setCount(count + num);
};
return (
<div>
<div>
<button onClick={() => handleCount(-1)}>-</button>
<button onClick={() => handleCount(+1)}>+</button>
<p>{count}</p>
</div>
</div>
);
};
export default Count;
カウントの数を「useState」で管理しているシンプルなプログラムですね。
こちら先に言っておくと別に何も問題はありませんし、正常に動作することには間違いありません。
多くの学習教材では全てと言って良いほど上記のコードで共有されていて、自分も全くもって同じ書き方で始まりました。
しかしカウントアップのみのアプリだから良いのですが、他の機能も含めた全体としては少しばかり怖い点があります。
useStateで管理するステートは書き方をミスるとハマる
先ほどのコードに少しばかり意地悪な修正をしてみたいと思います。
クリックしたときの関数を下記のようにダブルにしてみましょう。
import React, { useState } from "react";
const Count = () => {
const [count, setCount] = useState(0);
const handleCount = (num) => {
setCount(count + num);
setCount(count + num); //こちらを追加して処理をダブルにする
};
return (
<div>
<div>
<button onClick={() => handleCount(-1)}>-</button>
<button onClick={() => handleCount(+1)}>+</button>
<p>{count}</p>
</div>
</div>
);
};
export default Count;
何も知らない自分は単純に±2されると思っていました。
しかし実際には先ほどと同じく±1しかされません。
エラーではないのですが思ったような動作は実現できていないのです。
理由は後で解説するので先に進みます。
ステート関数は出来るだけ関数型でやろう
続いて下記のように修正してみます。
import React, { useState } from "react";
const Count = () => {
const [count, setCount] = useState(0);
const handleCount = (num) => {
setCount((currentCount) => {
return currentCount + num;
});
setCount((currentCount) => {
return currentCount + num;
});
};
return (
<div>
<div>
<button onClick={() => handleCount(-1)}>-</button>
<button onClick={() => handleCount(+1)}>+</button>
<p>{count}</p>
</div>
</div>
);
};
export default Count;
同じく処理をダブルにしているのですが、setCountの書き方が変わっています。
ステート変数のcountを単純に足す、引くのではなく、関数の形にして引数にcurrentCountを取ってcurrentCountとの計算をreturnで戻り値として出すようにしています。
「前の数字をもとにして計算する」ことをやっているのです。
こちらにすると期待通り±2の動きになります。
動作部分については冒頭に動画を添付しているので気になる方は見てみてください。
先ほどとの書き方だと「ステートを上書き」することになってしまうんですね。
とはいえ±2をしたいのであれば、handleCountの引数に2を渡せば済むので上記のコードは実際に使うことはないでしょう。
今回お伝えしたかったのは、出来ることならステート関数は「関数型」で書こう、ということです。
関数型にすることで引数と戻り値で「値を上書きするか、引き継ぐか」が明確になりますし、後からの追加実装にも少しばかり耐えやすくなります。
カウントアップのような簡単なプログラムほど、「簡単に終わらせたい」と思ってしまうところですが実務を想定した書き方を常に意識するように自分への戒めを込めて共有させて頂きました。