先に要点
- React は、画面を部品ごとに分けて作るための JavaScript ライブラリです(2026年6月時点の安定版は React 19.2 系)。
- 初心者が最初に押さえたいのは、コンポーネント・props・state・イベント処理の4つです。
- props は親から渡される値、state は自分の中で変わる値。この2つが混ざると「どこを直せば画面が変わるのか分からない」状態になります。
- 初心者が必ず一度はやる事故が「state を直接書き換えたのに画面が変わらない」です。原因と直し方を本文で具体的に示します。
「React ってよく聞くけど、結局なにをするものなの?」「JavaScript と何が違うの?」
フロントエンドを学び始めると、かなり早い段階で React の名前が出てきます。ただ、入門記事によっては最初から用語が多くて、コンポーネント・props・state・Hooks が一気に出てきて混乱しやすいです。
この記事では、React 公式 Learn / Quick Start(react.dev)を確認しながら、React を初めて触る人向けに「まず何が分かればよいか」を絞って整理します。さらに、この記事の後半では、入門書ではさらっと流されがちな props と state が混ざる失敗と、state を直接書き換えて再描画されない失敗を、実際のコードの before/after で具体的に示します。ここがいちばん詰まる場所だからです。
あとで Next.js や Zustand に進む前提でも、ここを押さえておくとかなり楽になります。
Reactとは何か
React は、画面を小さな部品に分けて組み立てるための JavaScript ライブラリです。ざっくり言うと「同じ見た目や動きをする部品を、再利用しやすくするための道具」です。
たとえば、ブログや業務画面を作るときには、次のような部品が何度も出てきます。
- ボタン
- ヘッダー
- 記事カード
- メニュー
- 入力フォーム
これらを毎回バラバラに書くのではなく、部品として作って使い回すのが React の基本です。
なお、React は単体では「画面を作る部分」だけを担当します。ルーティングや配信、サーバー側の処理は別の仕組み(たとえば Next.js)と組み合わせます。React 自体は SPA 的な作り方とも、SSR や SSG を含む枠組みとも組み合わせられる、柔軟な土台だと考えると整理しやすいです。
JavaScriptだけで作るのと何が違うのか
JavaScript だけでも画面は作れます。ただ、画面が大きくなると、
- どこで何を書き換えているのか分かりにくい
- 同じ UI を何度もコピペしやすい
- 状態が変わったときの更新が追いにくい
という問題が出やすくなります。
具体的に言うと、素の JavaScript では「データが変わったら、自分で document.querySelector して該当する DOM を書き換える」処理を、画面が変わるたびに手で書きます。ボタンの数字を1つ増やすだけでも、状態を持つ変数と、それを画面に反映する DOM 操作を、別々に同期させ続ける必要があります。これがズレると「変数は増えてるのに画面の数字が増えない」というバグになります。
React は、こうした「データと画面のズレ」を、コンポーネントと状態(state)の仕組みで自動的に合わせてくれます。「state を変えれば、画面はそれに合わせて自動で描き直される」。これが React の一番おいしいところです。
まず理解したい4つの基本
初心者は、最初から全部覚えなくて大丈夫です。まずは次の4つが分かれば、かなり前に進めます。
1. コンポーネント
画面を部品として分ける考え方です。ボタンやカード、一覧などを再利用しやすくします。
2. props
親コンポーネントから子コンポーネントへ渡す値です。子の側からは変更できない「読み取り専用」の入力です。
3. state
そのコンポーネントの中で変わる値です。開閉状態、入力値、選択中の項目などに使い、変えると再描画が起きます。
4. イベント処理
クリックや入力など、ユーザー操作に反応して state を変える処理です。
この4つを軸に見ると、React の全体像がかなり分かりやすくなります。とくに props と state の違いは、次の表のように「誰が変えられるか」「変えると何が起きるか」で並べると区別しやすいです。
| 観点 | props | state |
|---|---|---|
| どこから来る値か | 親コンポーネントから渡される | そのコンポーネント自身が持つ |
| 誰が変えられるか | 親だけ(子は変えてはいけない) | 自分(set 関数で変える) |
| 変えると何が起きるか | 親が変えれば子に再描画が伝わる | set 関数を呼ぶと再描画される |
| たとえると | 外から受け取る「入力」 | 自分の中の「メモ帳」 |
| 代表例 | 商品名・価格・タイトル | お気に入り中か・入力中の文字・開閉状態 |
コンポーネントとは何か
React では、画面を「部品」として分けます。たとえば記事一覧ページなら、こんなふうに分けられます。
- ページ全体
- 記事カード
- カテゴリラベル
- 検索フォーム
- ページネーション
このとき、記事カードを1つのコンポーネントにしておけば、一覧の中で何度も再利用できます。
function ArticleCard() {
return (
<article>
<h2>React入門</h2>
<p>初心者向けに基本を整理した記事です。</p>
</article>
);
}
もちろん、これだけだと毎回同じ内容しか出ません。そこで次に出てくるのが props です。
propsとは何か
props は、親から子へ渡す値です。同じコンポーネントでも、渡す値を変えることで中身を変えられます。
function ArticleCard(props) {
return (
<article>
<h2>{props.title}</h2>
<p>{props.excerpt}</p>
</article>
);
}
使う側ではこう書けます。
<ArticleCard
title="React入門"
excerpt="コンポーネント・props・stateの基本を整理します。"
/>
実務では、引数の props. を毎回書くのは面倒なので、分割代入で受け取る書き方が主流です。中身は同じです。
function ArticleCard({ title, excerpt }) {
return (
<article>
<h2>{title}</h2>
<p>{excerpt}</p>
</article>
);
}
ここで一番大事なルールは、props は子の側で書き換えてはいけないということです。props は「親から借りている値」であって、子が勝手に変えると、親が持っている本当の値とズレてバグになります。子の中で値を変えたいなら、それは props ではなく state の出番です。
stateとは何か
state は、そのコンポーネントの中で変わる値です。たとえば次のようなものは state の代表例です。
- メニューが開いているか
- フォームに何が入力されているか
- タブのどれが選ばれているか
- 「もっと見る」で件数を増やしているか
React では、useState を使ってこうした値を持てます。
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>増やす</button>
</div>
);
}
この例で大事なのは、count が変わると画面も変わることです。setCount を呼ぶと、React は「state が変わった」と判断して、そのコンポーネントを必要なところだけ再描画します。逆に言うと、再描画が起きるのは set 関数(ここでは setCount)を呼んだときだけです。ここが後半の落とし穴に直結します。
propsとstateの違いをもう少し直感的に言うと
たとえば、店の商品カードを考えると分かりやすいです。
- 商品名や価格を親が渡す → props
- 「お気に入りにしたか」をそのカード内で持つ → state
つまり、props は受け取る情報、state は変化を持つ情報です。次の章では、この区別が崩れた実際のコードを before/after で見ていきます。
落とし穴その1:propsとstateが混ざる(実コードのbefore/after)
入門でいちばん多い事故が、「親から props でもらった値を、子の中で書き換えようとする」ことです。やりたいこと自体(編集できる入力欄を作る)は正しいのに、props と state の役割が混ざっているために動かない、という形でハマります。
悪い例(Before):propsを直接書き換えようとする
ユーザー名を表示して、その場で編集できる入力欄を作りたい、という想定です。
function NameInput({ name }) {
return (
<input
value={name}
onChange={(e) => {
// props を直接書き換えようとしている(NG)
name = e.target.value;
}}
/>
);
}
このコードは、入力しても文字がまったく変わりません。
- 現象:キーボードを打っても入力欄の文字が変わらない(freezeしているように見える)。
- 原因:
nameは親から渡された props で、子の側でname = ...と代入しても React はそれを「state の変化」として認識しません。再描画も起きないので、value={name}は最初の値のまま固定されます。props はそもそも子から変えてはいけない値です。 - 確認手順:React DevTools で
NameInputを選び、Props 欄のnameが入力しても変化していないことを見ます。さらに、コンポーネントの先頭にconsole.log('render', name)を置き、入力しても再描画ログが出ないことを確認します。 - 回避:編集できる値は state にします。親からは「初期値」だけを props で受け取り、編集中の値はコンポーネント自身の state で持ちます。
良い例(After):初期値はprops、変わる値はstate
import { useState } from 'react';
function NameInput({ initialName }) {
// 編集中の値は state として自分で持つ
const [name, setName] = useState(initialName);
return (
<input
value={name}
onChange={(e) => setName(e.target.value)}
/>
);
}
ポイントは2つです。1つめは、親から渡すのは「初期値(initialName)」だと名前で分かるようにしたこと。2つめは、変わる値は useState で state にして、変更は必ず setName を通すこと。こうすると入力のたびに再描画が起き、画面に反映されます。
「親が持っている値を、子で変えて、その結果を親にも返したい」場合は、親が state を持ち、子には「値」と「変更を知らせる関数」の両方を props で渡すのが定石です(lifting state up と呼ばれます)。
function Parent() {
const [name, setName] = useState('');
return <NameInput value={name} onChange={setName} />;
}
function NameInput({ value, onChange }) {
return <input value={value} onChange={(e) => onChange(e.target.value)} />;
}
これで「state は親が1つだけ持つ」「子は表示と通知だけ」と役割が分かれ、混ざらなくなります。
落とし穴その2:stateを直接書き換えて再描画されない
もう1つの定番事故が、state を set 関数を通さずに直接書き換えることです。とくに配列やオブジェクトの state で起きます。React は「state が前と違う参照になったか」で再描画するかどうかを判断するため、元の配列を直接いじっても変化と見なされず、画面が更新されません。
悪い例(Before):配列をpushして再描画されない
「追加」ボタンを押すとリストに項目が増える、という想定です。
import { useState } from 'react';
function TodoList() {
const [todos, setTodos] = useState(['牛乳を買う']);
function add() {
// 既存の配列を直接書き換えている(NG)
todos.push('卵を買う');
setTodos(todos); // 同じ配列を渡しているので変化と見なされない
}
return (
<div>
<ul>{todos.map((t, i) => <li key={i}>{t}</li>)}</ul>
<button onClick={add}>追加</button>
</div>
);
}
- 現象:ボタンを押しても画面のリストが増えない。なのにリロードしたり別の操作で再描画が走ると、まとめて増えていることがある。
- 原因:
todos.push(...)は元の配列を書き換えるだけで、新しい配列を作りません。setTodos(todos)に渡しているのは「同じ参照」なので、React は「前と同じ=変わっていない」と判断し、再描画をスキップします。React の state 更新はイミュータブル(元を壊さず、新しい値を作って渡す)が前提です。 - 確認手順:
addの中でconsole.log(todos.length)を出すと、配列の中身自体は増えている(push は効いている)のに画面が変わらない、という食い違いが見えます。React DevTools の state 欄でも、クリック直後は更新が反映されません。 - 回避:既存の配列を壊さず、新しい配列を作って渡します。スプレッド構文(
...)が定番です。
良い例(After):新しい配列を作って渡す
function add() {
// 新しい配列を作って渡す(OK)
setTodos((prev) => [...prev, '卵を買う']);
}
オブジェクトの state でも同じです。obj.done = true のように直接書き換えるのではなく、新しいオブジェクトを作ります。
// NG:直接書き換え
user.name = '田中';
setUser(user);
// OK:新しいオブジェクトを作る
setUser((prev) => ({ ...prev, name: '田中' }));
さらに、関数型の更新(setState(prev => ...))を使う理由も覚えておくと役立ちます。同じ処理の中で続けて2回更新したいとき、setCount(count + 1) を2回書いても、どちらも同じ古い count を参照するため結局1しか増えません。setCount(prev => prev + 1) なら最新の値を基準に計算されるので、ちゃんと2増えます。
| やりたいこと | NG(直接書き換え) | OK(新しい値を作る) |
|---|---|---|
| 配列に追加 | arr.push(x); setArr(arr) |
setArr(prev => [...prev, x]) |
| 配列から削除 | arr.splice(i, 1) |
setArr(prev => prev.filter((_, idx) => idx !== i)) |
| オブジェクトの一部を変更 | obj.k = v; setObj(obj) |
setObj(prev => ({ ...prev, k: v })) |
| 数値を続けて2回増やす | setN(n + 1); setN(n + 1) |
setN(p => p + 1); setN(p => p + 1) |
イベント処理とは何か
React では、クリックや入力に応じて処理を書けます。これがイベント処理です。
function LikeButton() {
const [liked, setLiked] = useState(false);
return (
<button onClick={() => setLiked(!liked)}>
{liked ? 'お気に入り済み' : 'お気に入りする'}
</button>
);
}
ここでは、次の流れで動いています。
React は、この「操作 → set 関数で state 変更 → 自動で再描画」という流れがすべての基本です。逆に言えば、set 関数を呼ばずに値だけ変えても画面は変わらない(前の章の落とし穴)ことが、ここからも分かります。
なお、初心者がよくやる細かいミスとして、onClick={handleClick()} のように関数に () を付けてしまう、というものがあります。これだと「クリックされたとき」ではなく「描画したその瞬間」に実行されてしまい、ボタンを押す前に動く(しかも再描画のたびに動く)バグになります。渡すのは関数そのものなので、onClick={handleClick} か onClick={() => handleClick()} と書きます。
React初心者が最初につまずきやすいところ
1. propsとstateが混ざる
前述のとおり、これがかなり多いです。「外からもらう値(props)」と「自分で変える値(state)」が混ざると、何をどこで変えるべきか分からなくなります。迷ったら「この値、子が変える必要ある?」と自問し、変えるなら state、表示するだけなら props と切り分けます。
2. JavaScriptの文法で止まる
React が難しいというより、実は JavaScript の分割代入、配列メソッド(map / filter)、オブジェクト、アロー関数、スプレッド構文で止まっていることがあります。前章の [...prev, x] や { ...prev, k: v } が読めないなら、それは React ではなく JavaScript の補強が先です。
3. いきなり大きなアプリを作ろうとする
ログイン、API、検索、状態管理、ルーティングを最初から全部入れると、一気に分からなくなります。最初は、ボタン・カウンター・タブ切り替え・フォーム入力・一覧表示くらいの小さな UI から始める方が定着しやすいです。
実務ではReactをどう使うのか
実務で React がよく使われるのは、「画面部品が多く、更新も多い Web アプリ」です。
- 管理画面
- ダッシュボード
- 会員向け画面
- 検索画面
- 条件切り替えが多い UI
要するに、ただの静的ページより、ユーザー操作に応じて表示がよく変わる画面と相性がよいです。
一方で、SEO や配信まで含めて考えたい公開サイトでは、React 単体より Next.js のような枠組みまで使うことが多くなります。Next.js はルーティング・SSR / SSG・Vercel などへの配信(デプロイ)までまとめて面倒を見てくれるので、公開サイトでは定番です。配信時には CDN でファイルを配り、DNS で独自ドメインをつなぐ、という流れになります。
最初はどこまで分かれば十分か
初心者の最初の目標は、次の状態です。
- コンポーネントを作って分けられる
- props で値を渡せる(子では変えないと分かっている)
- useState で簡単な状態を持てる
- 配列・オブジェクトの state を「新しい値を作って」更新できる
- クリックや入力で表示を変えられる
- 小さな一覧画面やフォームを作れる
ここまでできれば、React 入門としてはかなり十分です。とくに4番目の「イミュータブルに更新できる」が一度腹落ちすると、再描画されないバグでほとんど詰まらなくなります。そのあとで、ルーティング・API 通信・状態管理・Next.js に進めばよいです。最初から Redux や大規模設計まで追う必要はありません。
次に何を学ぶべきか
React の次に進む方向は、やりたいことで変わります。
画面中心のWebアプリを作りたい
この場合は、Next.js に進むのが自然です。React の上に、ルーティング、SSR、配信の仕組みが入ってきます。
状態管理を整理したい
複数の画面や部品で state を共有したくなったら、Context API や Zustand が候補になります。「同じ state を、離れた複数のコンポーネントが見たい」と感じ始めたら検討のサインです。
APIとつなぎたい
外部 API や自作 API とつないで、一覧取得や登録処理を作る流れに進むと、実務感がかなり出てきます。
React入門に関するよくある質問
Q. React と JavaScript はどちらを先に学ぶべきですか?
A. JavaScript の基本(変数、関数、配列、オブジェクト、map/filter、スプレッド構文、async/await)を先に押さえる方が React の理解が早くなります。この記事のイミュータブル更新([...prev, x] など)がスッと読めるかが目安です。読めないうちは JavaScript の補強が先です。
Q. state を変えたのに画面が変わりません。なぜですか?
A. ほぼ100%、set 関数を通さず元の値を直接書き換えているか、同じ参照を渡しているかのどちらかです。arr.push() や obj.k = v のあとに同じ配列・オブジェクトを set している場合は、スプレッド構文で新しい値を作って渡してください。本文の「落とし穴その2」が該当します。
Q. props を子の中で書き換えてもいいですか?
A. いけません。props は親から借りている読み取り専用の値です。子で変えたい値は state にし、親側の値も変えたいなら「値」と「変更用の関数」を props で渡す(lifting state up)方法を使います。
Q. クラスコンポーネントは今でも学ぶ必要がありますか?
A. 新規学習者は関数コンポーネントと Hooks だけで十分です。古いコードベースを触る可能性がある場合のみ、後からクラス記法を学ぶで問題ありません。
Q. useState と useReducer はどう使い分けますか?
A. 状態が単純(true/false、文字列、数値)なら useState、複数のフィールドが連動する複雑な状態なら useReducer が向きます。慣れるまでは useState を中心に書いて、更新ロジックが膨らんできたら useReducer に切り替えます。
Q. useEffect は怖い、と聞きました。なぜですか?
A. 依存配列の指定ミス、無限ループ、クリーンアップ漏れなど、初心者がはまりやすいポイントが多いからです。データ取得には TanStack Query などのライブラリを使う方が安全、という流れになっています。入門段階では「画面の表示は state で完結させ、useEffect は後回し」で構いません。
Q. React で API を呼ぶには何を使うべきですか?
A. シンプルなら fetch + useState、本格的に作るなら TanStack Query か SWR を推奨します。キャッシュ、再フェッチ、エラーハンドリングを自前で書かなくて済みます。
Q. React の学習にどれくらい時間がかかりますか?
A. JavaScript の基礎ができている人なら、簡単な Todo アプリが書けるレベルまで20〜40時間が目安です。実務で詰まらないレベルまでは、3〜6か月の継続学習が現実的です。
まとめ
React は、画面を部品として分けて再利用しやすくするための JavaScript ライブラリです。初心者は、まずコンポーネント・props・state・イベント処理の4つに絞って理解するとかなり進みやすくなります。
そのうえで、この記事で見た2つの落とし穴を覚えておくと事故が激減します。1つめは、props を子で書き換えようとして動かない問題(変わる値は state にする)。2つめは、配列やオブジェクトの state を直接書き換えて再描画されない問題(新しい値を作って set 関数で渡す)。どちらも「state を変えるときは必ず set 関数を通し、元を壊さず新しい値を渡す」と覚えれば回避できます。
次に進むなら、公開サイトや Web アプリ全体まで含めて学びたい場合は Next.jsは難しい?初心者がつまずきやすいポイントと学ぶ順番を整理、状態管理を整理したいなら Zustandとは?Reactの状態管理ライブラリの使い方とRedux・Context APIとの違いを解説 がつながりやすいです。
参考リンク
- React Learn: https://react.dev/learn
- React Quick Start: https://react.dev/learn?quickstart=true
- Updating Objects in State(公式): https://react.dev/learn/updating-objects-in-state
- Updating Arrays in State(公式): https://react.dev/learn/updating-arrays-in-state
- useState リファレンス: https://react.dev/reference/react/useState