はじめに
関数コンポーネントのイベントリスナー内で、State(ステート/状態)が正しく扱えない場合の対処方法です。
React.useState()のステートを、イベントリスナーのコールバック関数内で参照しようとすると、値が最新の状態になっていないことがあります。
うまくいかない例
counterというStateを宣言- ボタンをクリックすると
counterに+1 - クリックに反応したイベントリスナーが
func()を発火 counterの値を出力
関数コンポーネント
// ①
const [counter, setCounter] = React.useState(0);
// ④
const func = () => {
console.log(counter);
};
// ③
React.useEffect(() => {
window.addEventListener("click", func);
}, []);
// ②
return (
<button onClick={() => setCounter(counter + 1)}>
ボタン
</button>
この例では、④ console.log(counter)で出力される値は常に0です。
イベントリスナーの登録時点のcounterの値で固定されてしまい、ステートが更新されても出力は変わりません。
これでは何回クリックしたかわからない…。
useRef()を使う
React.useRef()を使うことで、常に最新の状態を参照することができます。
うまくいく例
const [counter, setCounter] = React.useState(0);
// 追加
const counterRef = useRef(); // refオブジェクトを作成
counterRef.current = counter; // refはcounterを参照する
const func = () => {
// refを出力する
console.log(counterRef.current);
};
// -----以下は変更なし-----
React.useEffect(() => {
window.addEventListener("click", func);
}, []);
return (
<button onClick={() => setCounter(counter + 1)}>
ボタン
</button>
コールバック内でcounterを使うかわりに、counterRef.currentを使用します。
このようにすることで、最新のステートを参照することができます。
まとめ
関数コンポーネントのイベントリスナー内で、最新のstateが参照できない場合は、コールバック内でStateを使うかわりに、Stateを参照するRefを使用します。
このようにすることで、最新のステートを参照することができます。