※この記事は以下記事を参照し、自己学習用として書きました。参照記事が素晴らしいので、ぜひご覧ください。
参照記事
https://read-engineer.com/2020/12/11/react/#index_id2
目次
- なぜReactは挫折率が高いのか
- Reactを学ぶ前の準備
- Reactの5つのコア概念を理解する
- SPAとコンポーネント設計
- JSXの書き方
- 仮想DOMの仕組み
- StateとPropsによる状態管理
- React Hooksの基礎
- 実践!TodoアプリをReactで作り直す
- 次のステップへ
- まとめ
1. なぜReactは挫折率が高いのか
ReactはJavaScriptフレームワークの中で圧倒的なシェアを誇りますが、同時に挫折率も非常に高いフレームワークです。その理由は明確です。
JavaScriptの基礎が不十分なまま学習を始めてしまうからです。
Reactは「データを元にUIを自動更新する」技術ですが、その土台となるJavaScript(配列操作、非同期処理、モジュールなど)を理解していないと、何をしているのか全く分からなくなります。
逆に言えば、JavaScriptの基礎さえ固めてからReactに取り組めば、驚くほどスムーズに習得できます。段階を飛ばさず、確実に積み上げていきましょう。
2. Reactを学ぶ前の準備
Reactを始める前に、以下の3つを確認してください。
2-1. JavaScriptの基礎は身についていますか?
特に重要なのがこれらの知識です:
- 配列操作(map, filter, find)
- 非同期処理(Promise, async/await)
- ECMAScriptモジュール(import/export)
- アロー関数とthisの扱い
- オブジェクトの分割代入
これらが曖昧な場合は、まずJavaScriptの復習から始めましょう。
2-2. JavaScriptでTodoアプリを作れますか?
これが最も重要です。 素のJavaScriptでCRUD処理(作成・読込・更新・削除)を実装できる力があれば、Reactの学習はスムーズに進みます。
なぜなら、Reactは内部で自動的にデータ操作とUI更新を行いますが、その仕組みを理解するには「手動でやった経験」が必要だからです。
JavaScriptのみでTodoアプリを作ったことがない方は、まずそこから始めましょう。
2-3. ファンクションコンポーネントで学ぶ
Reactには2つの書き方があります:
- クラスコンポーネント(古い書き方)
- ファンクションコンポーネント(現在の主流)
2020年以降、React Hooksの登場でファンクションコンポーネントが主流になりました。クラスコンポーネントは複雑でthisの理解も必要なため、まずはファンクションコンポーネントから学びましょう。
3. Reactの5つのコア概念を理解する
3-1. SPAとコンポーネント設計
**SPA(シングルページアプリケーション)**とは、ページ全体を再読み込みせずに画面を切り替える技術です。ReactはSPA構築に最適なフレームワークです。
仕組みはシンプル:HTMLのbodyタグ以下の部分を入れ替えることで、画面遷移しているように見せているだけです。
通常のWebサイト:
ページA → サーバー通信 → ページB(全体を再読み込み)
SPA:
ページA → 表示部分だけ入れ替え → ページB(瞬時に切り替わる)
この入れ替えを実現するのがコンポーネントです。
コンポーネントとは、画面を構成する部品のことです。ボタン、ヘッダー、カード、フォームなど、パーツ単位で作成し、それらを組み合わせて画面を構築します。
画面全体
├── ヘッダー(コンポーネント)
├── メインコンテンツ(コンポーネント)
│ ├── サイドバー(コンポーネント)
│ └── 記事一覧(コンポーネント)
│ ├── 記事カード(コンポーネント)
│ ├── 記事カード(コンポーネント)
│ └── 記事カード(コンポーネント)
└── フッター(コンポーネント)
コンポーネントは入れ子にして使えます。導入される側を親コンポーネント、導入する側を子コンポーネントと呼びます。
3-2. JSXの書き方
ReactではJavaScriptの中にHTMLを書けます。この記法をJSXと呼びます。
// 通常のJavaScript
const element = document.createElement('h1');
element.textContent = 'Hello World';
// JSXを使ったReact
const element = <h1>Hello World</h1>;
JSXの基本ルール:
// シンプルなコンポーネント
function Greeting() {
const name = "太郎";
return (
<div>
<h1>こんにちは、{name}さん!</h1>
<p>今日もコーディング頑張りましょう</p>
</div>
);
}
// {}内にJavaScriptの式を埋め込める
function UserCard() {
const user = {
name: "花子",
age: 20,
isStudent: true
};
return (
<div>
<h2>{user.name}</h2>
<p>年齢: {user.age}歳</p>
<p>{user.isStudent ? "学生" : "社会人"}</p>
</div>
);
}
// classではなくclassNameを使う
function Button() {
return <button className="btn-primary">クリック</button>;
}
// 必ず1つの親要素で囲む
function Profile() {
return (
<div>
<h1>プロフィール</h1>
<p>詳細情報</p>
</div>
);
}
// または空のタグ(Fragment)を使う
function Profile2() {
return (
<>
<h1>プロフィール</h1>
<p>詳細情報</p>
</>
);
}
3-3. 仮想DOMの仕組み
Reactの最大の特徴が**仮想DOM(Virtual DOM)**です。これにより「変更箇所だけを効率的に更新する」ことが可能になります。
通常のWeb開発の問題点:
- データが変わるたびに画面全体を再描画
- パフォーマンスが悪い
- コードが複雑になりがち
Reactの解決策:
- メモリ上に仮想的なDOM(画面の設計図)を保持
- データが変わったら、新しい仮想DOMを作成
- 変更前と変更後の仮想DOMを比較
- 差分だけを実際のDOMに反映
[変更前の仮想DOM] vs [変更後の仮想DOM]
↓
差分を検知
↓
変更箇所だけ実際のDOMを更新(超高速!)
開発者は「データをどう変更するか」だけ考えればよく、画面更新の面倒な処理はReactが自動でやってくれます。
3-4. StateとPropsによる状態管理
Reactでは「データ(状態)が変わったら、自動的にUIが更新される」という仕組みで動きます。この状態を管理する2つの重要な概念があります。
State(ステート):コンポーネント内部の状態
コンポーネントが持つデータのことです。useStateで定義します。
import { useState } from 'react';
function Counter() {
// [現在の値, 値を変更する関数] = useState(初期値)
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1); // 状態を更新するとUIが自動で再描画される
};
return (
<div>
<p>カウント: {count}</p>
<button onClick={increment}>+1</button>
</div>
);
}
重要なポイント:
-
状態は直接変更してはダメ:
count = count + 1はNG - 必ずsetCount()を使う:これでReactが変更を検知してUIを更新
Props(プロップス):親から子へのデータ受け渡し
親コンポーネントから子コンポーネントへデータを渡す仕組みです。
// 親コンポーネント
function App() {
const userName = "太郎";
const userAge = 20;
return (
<div>
<UserProfile name={userName} age={userAge} />
</div>
);
}
// 子コンポーネント
function UserProfile(props) {
return (
<div>
<h2>{props.name}</h2>
<p>年齢: {props.age}歳</p>
</div>
);
}
// 分割代入でスッキリ書ける
function UserProfile({ name, age }) {
return (
<div>
<h2>{name}</h2>
<p>年齢: {age}歳</p>
</div>
);
}
StateとPropsの違い:
| State | Props | |
|---|---|---|
| 定義場所 | コンポーネント内部 | 親から受け取る |
| 変更 | できる | できない(読み取り専用) |
| 用途 | そのコンポーネントで管理するデータ | 親から子への情報伝達 |
3-5. React Hooksの基礎
React Hooksは、ファンクションコンポーネントで状態管理やその他の機能を使うための仕組みです。代表的なHooksを3つ紹介します。
useState:状態管理
先ほど説明した通り、コンポーネント内の状態を管理します。
function TodoInput() {
const [text, setText] = useState("");
return (
<input
value={text}
onChange={(e) => setText(e.target.value)}
/>
);
}
useEffect:副作用の処理
データ取得、タイマー、イベントリスナーの登録など、「レンダリング以外の処理」を行います。
import { useState, useEffect } from 'react';
function UserList() {
const [users, setUsers] = useState([]);
// コンポーネント表示時に実行される
useEffect(() => {
const fetchUsers = async () => {
const response = await fetch('https://api.example.com/users');
const data = await response.json();
setUsers(data);
};
fetchUsers();
}, []); // 空配列 = 初回のみ実行
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
依存配列の動き:
useEffect(() => { ... }, []); // 初回のみ実行
useEffect(() => { ... }, [count]); // countが変わったら実行
useEffect(() => { ... }); // 毎回実行(非推奨)
useContext:コンポーネント間の状態共有
Propsでデータをバケツリレーするのではなく、離れたコンポーネント間で直接データを共有できます。
import { createContext, useContext, useState } from 'react';
// コンテキストを作成
const UserContext = createContext();
// 親コンポーネント
function App() {
const [user, setUser] = useState({ name: "太郎", role: "admin" });
return (
<UserContext.Provider value={user}>
<Header />
<MainContent />
</UserContext.Provider>
);
}
// 深い階層の子コンポーネント
function UserDisplay() {
const user = useContext(UserContext);
return <p>ようこそ、{user.name}さん({user.role})</p>;
}
4. 実践!TodoアプリをReactで作り直す
JavaScript基礎編で作ったTodoアプリを、Reactで作り直してみましょう。これでReactの状態管理が体感的に理解できます。
完成イメージ
- タスクを追加・削除できる
- タスクの完了/未完了を切り替えられる
- 統計情報を表示する
コード全体
import { useState } from 'react';
import './App.css';
function App() {
const [todos, setTodos] = useState([]);
const [inputText, setInputText] = useState("");
const [nextId, setNextId] = useState(1);
// タスク追加
const addTodo = () => {
if (inputText.trim() === "") return;
const newTodo = {
id: nextId,
text: inputText,
completed: false
};
setTodos([...todos, newTodo]);
setNextId(nextId + 1);
setInputText("");
};
// Enter キーで追加
const handleKeyPress = (e) => {
if (e.key === 'Enter') addTodo();
};
// 完了状態の切り替え
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
));
};
// タスク削除
const deleteTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
// 統計計算
const totalCount = todos.length;
const completedCount = todos.filter(todo => todo.completed).length;
const remainingCount = totalCount - completedCount;
return (
<div className="container">
<h1>📝 Todo リスト(React版)</h1>
<div className="input-area">
<input
type="text"
value={inputText}
onChange={(e) => setInputText(e.target.value)}
onKeyPress={handleKeyPress}
placeholder="新しいタスクを入力..."
/>
<button onClick={addTodo}>追加</button>
</div>
<ul className="todo-list">
{todos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onToggle={toggleTodo}
onDelete={deleteTodo}
/>
))}
</ul>
<div className="stats">
全{totalCount}件 / 完了{completedCount}件 / 残り{remainingCount}件
</div>
</div>
);
}
// 子コンポーネント:個別のTodoアイテム
function TodoItem({ todo, onToggle, onDelete }) {
return (
<li className={todo.completed ? 'completed' : ''}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => onToggle(todo.id)}
/>
<span>{todo.text}</span>
<button onClick={() => onDelete(todo.id)}>削除</button>
</li>
);
}
export default App;
CSS(App.css)
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.container {
background: white;
border-radius: 15px;
padding: 30px;
width: 100%;
max-width: 500px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
}
h1 {
color: #667eea;
margin-bottom: 20px;
text-align: center;
}
.input-area {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.input-area input {
flex: 1;
padding: 12px;
border: 2px solid #e0e0e0;
border-radius: 8px;
font-size: 16px;
}
.input-area input:focus {
outline: none;
border-color: #667eea;
}
.input-area button {
padding: 12px 24px;
background: #667eea;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 16px;
font-weight: bold;
}
.input-area button:hover {
background: #5568d3;
}
.todo-list {
list-style: none;
margin-bottom: 20px;
}
.todo-list li {
display: flex;
align-items: center;
gap: 10px;
padding: 12px;
background: #f8f9fa;
border-radius: 8px;
margin-bottom: 10px;
transition: all 0.3s;
}
.todo-list li:hover {
background: #e9ecef;
}
.todo-list li.completed span {
text-decoration: line-through;
color: #999;
}
.todo-list input[type="checkbox"] {
width: 20px;
height: 20px;
cursor: pointer;
}
.todo-list span {
flex: 1;
font-size: 16px;
}
.todo-list button {
padding: 6px 12px;
background: #dc3545;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
}
.todo-list button:hover {
background: #c82333;
}
.stats {
text-align: center;
color: #666;
font-size: 14px;
padding: 10px;
background: #f8f9fa;
border-radius: 8px;
}
JavaScript版との違いを理解する
JavaScript版:
- DOMを直接操作(createElement, appendChild など)
- データが変わったら手動でHTMLを再構築
- イベントリスナーを手動で登録
React版:
- JSXで宣言的にUIを記述
- 状態(State)が変わったら自動でUIが更新される
- データの流れが分かりやすい
例えば削除機能を比較すると:
// JavaScript版
deleteTodo(id) {
this.todos = this.todos.filter(t => t.id !== id);
this.render(); // 手動で画面を再描画
}
// React版
const deleteTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
// 自動で再描画される!
};
重要ポイント3つ
-
配列の操作にはmap/filterを使う:Reactでは配列を直接変更せず、新しい配列を作って状態を更新します。
-
keyプロップは必須:リスト表示時は必ず
keyを指定します。Reactが効率的に差分を検知するために必要です。 -
イベントハンドラーで関数を渡す:
onClick={() => deleteTodo(todo.id)}のように、アロー関数で引数を渡します。
さらにレベルアップ:Context APIを使う
TodoアプリをContext APIでリファクタリングすると、Propsのバケツリレーを避けられます。
import { createContext, useContext, useState } from 'react';
// コンテキスト作成
const TodoContext = createContext();
// カスタムフック
const useTodos = () => {
const context = useContext(TodoContext);
if (!context) {
throw new Error('useTodos must be used within TodoProvider');
}
return context;
};
// プロバイダーコンポーネント
function TodoProvider({ children }) {
const [todos, setTodos] = useState([]);
const [nextId, setNextId] = useState(1);
const addTodo = (text) => {
const newTodo = { id: nextId, text, completed: false };
setTodos([...todos, newTodo]);
setNextId(nextId + 1);
};
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
));
};
const deleteTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
const value = {
todos,
addTodo,
toggleTodo,
deleteTodo
};
return (
<TodoContext.Provider value={value}>
{children}
</TodoContext.Provider>
);
}
// 使用例
function App() {
return (
<TodoProvider>
<TodoApp />
</TodoProvider>
);
}
function TodoApp() {
const { todos, addTodo, toggleTodo, deleteTodo } = useTodos();
// あとは同じように使える
// Propsを何階層も渡す必要がない!
}
5. 次のステップへ
Reactの基礎を押さえたら、次は実務で必要なスキルを身につけましょう。
5-1. TypeScriptを学ぶ
モダンなフロント開発では、JavaScriptではなくTypeScriptを使うのが主流です。型定義によってバグを事前に防げるため、ほぼ必須のスキルです。
// TypeScriptでのReact
interface Todo {
id: number;
text: string;
completed: boolean;
}
interface TodoItemProps {
todo: Todo;
onToggle: (id: number) => void;
onDelete: (id: number) => void;
}
function TodoItem({ todo, onToggle, onDelete }: TodoItemProps) {
// 型が保証されているので安全!
}
5-2. React Routerでルーティング
複数ページを持つSPAを作るには、React Routerが必要です。
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<nav>
<Link to="/">ホーム</Link>
<Link to="/about">About</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</BrowserRouter>
);
}
5-3. グローバル状態管理(Redux)
複数のコンポーネントで共通の状態を使いたい場合、ReduxやZustandなどのライブラリを使います。
Context APIでも実現できますが、大規模アプリではReduxの方が管理しやすいケースが多いです。
5-4. 実務レベルのアウトプット
簡単なTodoアプリができたら、次はこれらに挑戦しましょう:
- APIを使ったデータ取得:外部APIから天気情報やニュースを表示
- 認証機能付きアプリ:ログイン/ログアウト機能の実装
- リアルタイム通信:チャットアプリやコラボレーションツール
- レスポンシブデザイン:スマホ・タブレット対応
6. まとめ
Reactは一見難しそうですが、正しい順序で学べば必ず習得できます。重要なのは段階を飛ばさないことです。
今日から始める3ステップ:
-
JavaScript基礎の確認:配列操作、非同期処理、モジュールを復習。JavaScriptでTodoアプリを作る。
-
Reactの核心を理解:コンポーネント、JSX、State/Props、React Hooksの概念を掴む。完璧でなくてOK。
-
実践!作って学ぶ:TodoアプリをReactで作り直す。動いた瞬間の達成感が次のモチベーションになる。
挫折しないための心構え:
- 最初から全てを理解しようとしない
- エラーは成長のチャンス
- 小さく作って、少しずつ機能を追加
- 分からないことは調べながら進める
ReactはWeb開発の世界で最も求められているスキルの1つです。この記事で紹介した内容を実践すれば、確実にReactエンジニアへの道が開けます。
まずはJavaScriptでTodoアプリを作り、それをReactで作り直してみましょう。その経験が、あなたのキャリアを大きく変える第一歩になるはずです。頑張ってください!