0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

最低限覚えておくべき useState と useRef について

Last updated at Posted at 2025-12-14

これは

ReactのuseStateuseRefについてあまり理解できていなかったので最低限まとめておく。

useStateとuseRefの違い

ReactのuseStateuseRefの違いは、UIに出るかどうか再レンダリングを引き起こすかどうかでほぼ決まる。

useState

useStateは、画面に表示される値を管理するためのフックだ。
この値が更新されると、Reactは「状態が変わった」と判断して 再レンダリング(= コンポーネント関数の再実行) を行う。

また、前の値に依存して更新する場合は注意が必要で、setX(x + 1)のように直接変数を使うと更新がうまくいかないことがある。
setX(prev => prev + 1) の形で書くのが安全だ。(functional update)

functional update で安全に更新

import { useState } from "react";

export default function App() {
  const [count, setCount] = useState(0);

  const unsafe = () => {
    // この count は今のレンダリング時点の値
    setCount(count + 1);
    setCount(count + 1); // 同じ count をもう一度使っている
  };

  const safe = () => {
    // prev は常に最新の state
    setCount((prev) => prev + 1);
    setCount((prev) => prev + 1);
  };

  return (
    <div>
      <p>count: {count}</p>
      <button onClick={unsafe}>+2したい(危険)</button>
      <button onClick={safe}>+2(安全)</button>
    </div>
  );
}

前の値に依存する更新は必ず functional update を使う。
(危険)ボタンでは setCount を2回呼んでも +1 しかされない。
count を直接使う書き方は、古い値を掴んで更新がうまく反映されない原因になる。

useRef

useRefは、UIに直接出さない値DOM要素への参照を保持するためのフックだ。
ref.currentを更新しても、再レンダリングは発生しない

ただし、再レンダリングが起きても値は保持され続ける。
これはローカル変数との決定的な違いで、ローカル変数はコンポーネント関数が再実行されるたびに毎回作り直される。

useRefは保持される(でもUIは勝手に更新されない)

import { useRef, useState } from "react";

export default function App() {
  const [, forceRerender] = useState(0);
  const refCounter = useRef(0);

  return (
    <div>
      <p>refCounter.current: {refCounter.current}</p>

      <button
        onClick={() => {
          refCounter.current += 1;
        }}
      >
        ref +1(画面は変わらない)
      </button>

      <button onClick={() => forceRerender((p) => p + 1)}>
        再レンダリング(表示が追いつく)
      </button>
    </div>
  );
}

refCounter.currentは確かに増えているが、それだけでは画面は更新されない。
しかし、何らかの理由で再レンダリングが起きると、その時点で表示が追いつく。

refでDOM参照

import { useRef } from "react";

export default function App() {
  const boxRef = useRef<HTMLDivElement | null>(null);

  return (
    <div>
      <button onClick={() => boxRef.current?.scrollIntoView({ behavior: "smooth" })}>
        下のボックスへスクロール
      </button>

      <div style={{ height: 600 }} />

      <div ref={boxRef} style={{ padding: 16, border: "1px solid" }}>
        ここが対象のDOM
      </div>
    </div>
  );
}

<div ref={boxRef} /> と書くことで、実DOM要素が boxRef.current に入る
scrollIntoViewfocus はReactの機能ではなく、ブラウザが持っている標準APIだ。

まとめ

useStateは「画面に出る状態」を管理するためのもの、useRefは「画面に出さない値やDOM参照」を保持するためのもの。
前の値に依存する更新は必ず functional update を使う。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?