useState 完全入門【React】最初に覚えるべき状態管理フック

ReactのuseStateとは何か、state変数の作り方・更新関数の使い方・オブジェクトの扱い方を初心者向けに丁寧に解説。よくあるミスとその解決策も紹介します。

#react#hooks#usestate#javascript#frontend

Reactを学び始めたとき、最初にぶつかる概念が「state(状態)」です。そして state を扱うためのフックが useState です。

「ボタンをクリックしたら数が増える」「入力した文字が表示される」——こういったインタラクティブなUIを作るために useState は欠かせません。React の全フックの中で最も使う機会が多い、まさに基本中の基本です。

この記事でわかること:

  • state とは何か・なぜ必要か
  • useState の基本的な書き方
  • 更新関数の正しい使い方(直接更新 vs 関数形式)
  • オブジェクト・配列の state を更新するときの注意点
  • よくあるミスと解決策

state とは?

state は、**コンポーネントが持つ「記憶」**です。

ユーザーの操作によって変化する値(クリック回数・入力テキスト・表示/非表示のフラグなど)を state で管理します。state が変わると、React は自動でコンポーネントを再レンダリングし、画面を最新の状態に更新します。

// state なし:ボタンをクリックしても count は変わらない
function BadCounter() {
  let count = 0; // ← ただのローカル変数
  return <button onClick={() => count++}>{count}</button>;
}
 
// state あり:クリックするたびに画面が更新される
function GoodCounter() {
  const [count, setCount] = useState(0); // ← state
  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

基本的な書き方

const [state, setState] = useState(initialState);
要素説明
state現在の値
setStatestate を更新する関数
initialState初期値(数値・文字列・配列・オブジェクトなど何でも可)

慣例として [something, setSomething] という命名が使われます。

import { useState } from "react";
 
function LikeButton() {
  const [liked, setLiked] = useState(false); // 初期値は false
 
  return (
    <button onClick={() => setLiked(!liked)}>
      {liked ? "❤️ いいね済み" : "🤍 いいねする"}
    </button>
  );
}

更新関数の2つの書き方

直接値を渡す

setCount(5);         // count を 5 にする
setName("太郎");     // name を "太郎" にする

関数形式(前の値から計算する場合)

setCount(prev => prev + 1); // 前の値に1を足す

複数の更新が同時に発生する場面や、前の値を確実に参照したい場面では関数形式を使いましょう。

// ❌ 問題が起きやすい書き方
function handleClick() {
  setCount(count + 1);
  setCount(count + 1); // count がまだ古い値なので結果は +1 になってしまう
}
 
// ✅ 安全な書き方
function handleClick() {
  setCount(prev => prev + 1);
  setCount(prev => prev + 1); // 確実に +2 になる
}

初期値を関数で計算する

計算コストが高い初期値の場合、関数として渡す(遅延初期化)と効率的です。

// ❌ 毎回のレンダリングで createInitialState() が実行される
const [state, setState] = useState(createInitialState());
 
// ✅ 初回レンダリングのときだけ createInitialState が実行される
const [state, setState] = useState(createInitialState); // 関数自体を渡す

オブジェクトの state を更新する

オブジェクトを state にする場合、直接書き換えてはいけません。必ず新しいオブジェクトを作って渡します。

const [user, setUser] = useState({ name: "山田", age: 25 });
 
// ❌ NG:直接書き換えると React が変化を検知できない
user.name = "田中";
setUser(user);
 
// ✅ OK:スプレッド演算子で新しいオブジェクトを作る
setUser({ ...user, name: "田中" }); // nameだけ変えて他はそのまま

配列の state を更新する

配列も同様に、元の配列を書き換えずに新しい配列を作ります。

const [items, setItems] = useState(["りんご", "みかん"]);
 
// ✅ 追加:スプレッドで新しい配列を作る
setItems([...items, "ぶどう"]);
 
// ✅ 削除:filterで新しい配列を作る
setItems(items.filter(item => item !== "みかん"));
 
// ✅ 更新:mapで新しい配列を作る
setItems(items.map(item => item === "りんご" ? "青りんご" : item));
 
// ❌ NG:push/splice などは使わない(元の配列を書き換えてしまう)
items.push("ぶどう");
setItems(items);

よくあるミスと解決策

ミス1:更新直後に新しい値を参照しようとする

// ❌ 更新はすぐには反映されない
setCount(count + 1);
console.log(count); // まだ古い値が表示される
 
// ✅ 更新後の値を使いたい場合は変数に入れる
const newCount = count + 1;
setCount(newCount);
console.log(newCount); // 正しい値

ミス2:オブジェクトを直接変更してから set する

// ❌ NG
const newUser = user;
newUser.name = "田中"; // 同じオブジェクトを変更してしまっている
setUser(newUser); // React が変化を検知できず画面が更新されない
 
// ✅ OK
setUser({ ...user, name: "田中" }); // 新しいオブジェクトを作る

ミス3:ループや条件分岐の中で使う

// ❌ NG:Hooksのルール違反
if (isLoggedIn) {
  const [name, setName] = useState(""); // エラーになる
}
 
// ✅ OK:常にトップレベルで呼び出す
const [name, setName] = useState("");
if (!isLoggedIn) return null;

まとめ

  • useState はコンポーネントに「記憶」を持たせるフック
  • [state, setState] の形で分割代入して使う
  • 前の値を使って更新するときは setState(prev => ...) の関数形式を使う
  • オブジェクト・配列は直接変更せず、新しいものを作って渡す
  • 更新は非同期なので、直後に新しい値を参照することはできない

関連記事