React

【React.useRef】イベントリスナー内で最新のステートを参照できない時の対処法

React
この記事は約3分で読めます。

はじめに

関数コンポーネントのイベントリスナー内で、State(ステート/状態)が正しく扱えない場合の対処方法です。

React.useState()のステートを、イベントリスナーのコールバック関数内で参照しようとすると、値が最新の状態になっていないことがあります。

うまくいかない例

  1. counterというStateを宣言
  2. ボタンをクリックするとcounter+1
  3. クリックに反応したイベントリスナーがfunc()を発火
  4. 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を使用します。

このようにすることで、最新のステートを参照することができます。