10
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【React】「useState」の基本を理解しよう!~再レンダリングと更新関数の罠~

10
Last updated at Posted at 2025-12-01

はじめに

こんにちは、watnowの滝村です。この記事は、watnow Advent Calendar 2025 の2日目の記事です。

今回は、僕がReactを勉強していて一番最初に躓いた、Hooksの代表格である 「useState」 について説明していきたいと思います。

そもそも useState とは

ざっくり言うと、コンポーネントに「状態(データ)」を持たせ、変更があったら画面を自動で書き換える(再レンダリングする)ための機能(Hooks) です。

ちなみにHooksとは、関数コンポーネントでReactの便利な機能(状態管理やライフサイクルなど)を使うための関数の総称です。

なぜ変数じゃダメなの?

let などの普通の変数だと、変数の値が変わってもReactはそれに気づけません。そのため、内部でデータが変わっても画面上の数字はずっと古いまま……ということが起きてしまいます。 「データが変わったら、画面も更新してほしい!」という時に useState が必要になります。

基本的な使い方

まずは書き方を見てみましょう。

count.jsx
import { useState } from "react";

// const [今の値, 更新するための関数] = useState(初期値);
const [count, setCount] = useState(0);

このように、分割代入を使って受け取ります。

  • 第1引数 count : 現在の値(読み取り専用)。

  • 第2引数 setCount : 値を更新するための関数。これを使うと再レンダリングが走ります。 慣例として set○○ と書かれることが多いです。

  • 初期値 0: 最初に画面に表示される値。

実践:カウントアップボタン

ボタンを押すと数字が増えるシンプルなコンポーネントを作ってみます。

countUp.jsx
"use client"

import { useState } from "react";

export default function CountUp() {
  const [count, setCount] = useState(1);
  
  const handleClick = () => {
    // 現在の count に 1 を足した値で更新する
    setCount(count + 1);
  };
  
  return (
    <div>
      <p>現在のカウント: {count}</p>
      <button onClick={handleClick}>クリック</button>
    </div>
  );
  
}

これでボタンを押せば数字が増えていきます。しかし、実はこの書き方setCount(count + 1)には落とし穴があります。

更新関数の罠と解決策

よくある間違い

「ボタンを1回押したら、一気に3増えるようにしたい」と思って、以下のように書いたとします。

countUp.jsx
const [count, setCount] = useState(1);

const handleClick = () => {
  setCount(count + 1);
  setCount(count + 1);
  setCount(count + 1);
}

handleClick が実行されると、countの中身は 1+3 で 4 になると思いますよね? しかし実際は、countの中身は 2 にしかなりません。

なぜ起きるのか?

これは、handleClick 関数が実行された瞬間の count の値が 「1」 だからです。Reactへの命令は以下のようになります。

  1. 1つ目の setCount(1 + 1) が予約される
  2. 2つ目の setCount(1 + 1) が予約される(まだ画面上の count は 1 のまま!)
  3. 3つ目の setCount(1 + 1) が予約される

結果として、Reactは「あ、全部 2 にしたいのね」と判断して、最終的に「2」にしかなりません。

解決策:関数型アップデート

これを防ぐために、更新関数には「新しい値」を渡すのではなく、「直前の値を受け取って計算する関数」を渡します。これを関数型アップデートと呼びます。

countUp.jsx
// 良い書き方
const handleClick = () => {
  setCount((prevCount) => prevCount + 1); // 1 -> 2
  setCount((prevCount) => prevCount + 1); // 2 -> 3
  setCount((prevCount) => prevCount + 1); // 3 -> 4
};

こうすることで、常に最新の状態(直前の計算結果) を受け取って処理できるため、正しく 4 になります。

「ボタン連打」などの激しい操作や、複雑なアプリになった時にバグを防ぐためにも、「今の値を使って新しい値を計算する」ときは、この関数型アップデートを使うのが鉄則です。

その他の活用例

最後に、カウンター以外のよくある使い方を紹介します。

テキスト入力フォーム

入力された文字をそのまま保存する場合は、値を直接渡すパターンでOKです。

textInput.jsx
"use client"

import { useState } from "react";

export default function TextInput() {
  const [name, setName] = useState("");

  const handleName = (e) => {
    // 入力欄の値をそのままセットする
    setName(e.target.value);
  };

  return (
    <div>
      <input type="text" onChange={handleName} value={name} />
      <p>入力された名前: {name}</p>
    </div>
  );
}

表示/非表示を切り替える(トグル)

「表示されているなら非表示に」「非表示なら表示に」という処理は、現在の状態true/false に依存するので、関数型アップデートを使います。

toggle.jsx
"use client"

import { useState } from "react";

export default function Toggle() {
  const [open, setOpen] = useState(false);

  const toggle = () => {
    // 今の状態(open)を受け取り、その逆(!open)を返す
    setOpen((prevOpen) => !prevOpen);
  }

  return (
    <div>
      <button onClick={toggle}>
        {open ? "隠す" : "表示する"}
      </button>
      {open && <p>ここに秘密のメッセージが表示されます!</p>}
    </div>
  );
}

おわりに

今回はReact開発の基本である useState について解説しました。

  • 値が変わると画面も変わる(再レンダリング)
  • 更新するときは「値を渡す」か「関数を渡す」かの2パターンある
  • 今の値を使って計算するときは「関数を渡す」ほうが安全

このあたりを覚えておけば、基本はばっちりです! 他にもReactには useEffect など様々なHooksが存在します。まずは useState を使いこなして、色々なUIを作ってみてください。

最後まで読んでいただきありがとうございました!

参考

10
1
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
10
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?