ReactのAPI POST処理:postJsonでフォームを送信する
入力検証、JSON送信、二重送信の抑制、HTTPエラー、204応答とサーバー側の責任を分けて解説します。
React入門をJavaScriptの基本から学ぶガイド。UIライブラリの役割、Viteでの環境構築、JSX・コンポーネント・props・useStateを解説します。チェックリストの完成コードと図解で、入力・追加・削除・状態管理を確認。エラー対処、公式資料、次に読む実践記事も紹介します。
内容更新 · 執筆・確認:y_ymo10
ReactはUIを作るJavaScriptライブラリです。このページの入門解説では、Viteで環境を構築し、JSX・props・useStateを使ったチェックリストを作ります。まず動かし、入力・追加・完了・削除の仕組みを確認しましょう。
初めての方は第1回から。復習したいテーマからも読めます。
12 ARTICLES
入力検証、JSON送信、二重送信の抑制、HTTPエラー、204応答とサーバー側の責任を分けて解説します。
API取得を独自Hookへ切り出し、読み込み・失敗・成功、中止、古い通信結果の抑止、再取得を実装します。
fetchでJSONを取得する独自関数を作り、HTTPエラー、JSON解析失敗、AbortSignal、取得データの検証を整理します。
useStateを独自Hookにまとめ、開閉・明示的な開く/閉じる操作と、呼び出しごとの状態の独立性を学びます。
通常の関数とHooksの違いを整理し、入力文字列の整形・イベント処理・純粋関数のテストを実例で学びます。
ReactのFragment・StrictMode・Suspense・lazyを用語集の実装で解説。key付きFragment、遅延読み込み、開発時の追加チェック、待機と失敗の違いを学びます。
useTransitionとuseDeferredValueを検索画面のコードで比較。入力と結果更新の優先度、更新中の表示、デバウンスとの違いをReact 18対応の例で学びます。
ReactのuseIdで重複しないIDを生成し、入力欄・ラベル・説明文を関連付ける方法を解説。TypeScriptのフォーム例、keyとの違い、SSR時の注意点を学べます。
ReactのuseMemo・useCallback・memoの違いを商品検索のコードで解説。依存配列、propsの参照、React Compilerとの関係、計測して最適化する判断を学びます。
React公式リファレンスを参考に、useReducerで買い物リストを実装。TypeScriptのaction型、dispatch、純粋な更新関数、追加・購入済み切り替え・削除を学びます。
ReactのuseState・useEffect・useRef・useContextをTypeScriptの動く例で解説。状態更新、イベントの後始末、入力欄へのフォーカス、テーマ共有と使い分けを学びます。
React.jsのコンポーネント・props・stateを、検索と読了切り替え付きの読書リストで学びます。TypeScriptの完全なコード例、配列更新、key、useEffectの判断を解説。
内容更新 · 執筆・確認:y_ymo10
React入門で最初に覚えたいのは、画面をコンポーネントに分け、propsでデータを渡し、stateの変化に合わせて表示を更新する流れです。この解説ではJavaScriptで書く小さなチェックリストを題材に、環境構築から操作の確認まで進めます。先に完成コードを動かしてから、分からない用語に戻って読んでも構いません。
Reactはユーザーインターフェースを組み立てるJavaScriptライブラリです。WebページではHTMLが内容の構造、CSSが見た目、JavaScriptが計算や操作への応答を担当します。Reactを使ってもこの役割は残り、画面の一部分を「データを受け取り、表示内容を返す部品」として扱えるようになります。
例えば商品一覧なら、ページ全体、検索欄、商品カードを別々のコンポーネントにできます。同じカードを何度も書く代わりに、商品名や価格を渡して再利用します。一方、文章を載せるだけの小さなページでは、通常のHTMLとCSSで十分な場合もあります。入力や選択によって複数の表示が変わる画面を作ると、Reactの役割を理解しやすくなります。
学習前に、変数、関数、配列、オブジェクト、分割代入、map()、importとexportを確認しましょう。すべて暗記する必要はありません。サンプルを読んで「ここで関数を呼ぶ」「この配列から一覧を作る」と説明できれば、Reactの学習と並行して復習できます。JavaScriptの関数・モジュールと配列・オブジェクトが復習に使えます。
ReactはUIの部品と更新を扱います。Viteは開発サーバーやビルドを提供するツールです。Next.jsなどのフレームワークは、Reactに加えてページのルーティングやサーバー側の処理などをまとめて扱います。「Reactを学ぶこと」と「本番アプリの構成を決めること」は分けて考えましょう。
本ページでは仕組みを学ぶためにViteを使います。ルーティング、認証、データ取得、SSRが必要なサービスでは、フレームワークを使う構成も検討します。React公式にも、学習目的などでReactアプリをゼロから構築する方法が説明されています。Next.jsとの関係はNext.js入門ガイドでも確認できます。
Node.jsとnpm、コードを編集するエディター、ブラウザーを用意します。ターミナルで次のコマンドを順に実行してください。最初の2行はインストール済みのバージョン確認です。Viteが対応するNode.jsの条件は更新されるため、Vite公式の環境構築ガイドとインストール時の表示を確認してください。
node --version
npm --version
npm create vite@latest react-first-app -- --template react
cd react-first-app
npm install
npm run dev生成時にパッケージの取得確認が出た場合は、表示された内容を確認して進みます。react-first-appは新しく作るフォルダー名です。起動後はターミナルに表示されたローカルURLをブラウザーで開きます。既に同じポートを使っていると別の番号になるため、URLを固定して覚える必要はありません。終了するときはターミナルでCtrl+Cを押します。
この例はJavaScript用のreactテンプレートです。TypeScriptを使う場合はreact-tsを選びますが、本ページのコードは.jsx用です。まずはJavaScript版で表示と操作を確認し、その後にpropsやイベントの型を追加すると、Reactと型の問題を切り分けやすくなります。
index.htmlはブラウザーに読み込ませる入口です。src/main.jsxがReactのルートを作り、src/App.jsxでアプリの内容を定義します。src/App.cssやsrc/index.cssは見た目の設定、package.jsonは依存パッケージと実行コマンド、package-lock.jsonは解決された依存関係を記録するファイルです。
npm run devは編集しながら確認するためのコマンドです。完成後の出力はnpm run buildで作り、標準構成ではdistフォルダーに生成されます。npm run previewでビルド結果をローカル確認できますが、本番配信サーバーの代わりとして常用するものではありません。作業中に問題が起きたら、ブラウザーのエラーだけでなく、開発サーバーを起動したターミナルの表示も読みましょう。
JSXではタグの形でUIを書き、{}の中にJavaScriptの式を入れます。HTMLを文字列として組み立てる方法とは異なり、ビルドツールがReactで扱えるコードへ変換します。CSSクラスはclassName、ラベルの関連付けはhtmlForを使い、inputのような要素も<input />と閉じます。
コンポーネント名はLessonCardのように大文字で始めます。小文字のdivやbuttonは組み込みのHTML要素として扱われるためです。戻り値に並列の要素がある場合は、共通の親要素かFragmentの<>...</>でまとめます。書き方の詳細はReact公式のJSX解説を参照してください。
次のコードは単独で試せる例です。src/App.jsxを置き換えると、同じLessonCardから内容の違う2つのカードが表示されます。propsは親から子へ渡す入力であり、子が受け取った値を直接書き換えるためのものではありません。
function LessonCard({ title, minutes }) {
return (
<section>
<h2>{title}</h2>
<p>学習の目安:{minutes}分</p>
</section>
);
}
export default function App() {
return (
<main>
<h1>今日の学習</h1>
<LessonCard title="JSXを書く" minutes={15} />
<LessonCard title="イベントを扱う" minutes={20} />
</main>
);
}title="JSXを書く"は文字列、minutes={15}は数値を渡しています。値に応じて表示が変わる一方、見出しと段落の構造は共通です。見た目の似た部分を無条件に細分化するより、繰り返し使う単位や、独立した役割を持つ部分から部品にすると読みやすくなります。propsの公式解説と読書リストの実践記事も参考にしてください。
stateはコンポーネントが次のレンダーでも覚えておく値です。useStateは現在の値と更新関数を返します。通常のローカル変数を書き換えるだけではReactに画面更新を伝えられないため、クリック回数や入力内容には更新関数を使います。stateの仕組みを押さえると、画面が変わる理由を追いやすくなります。
次もsrc/App.jsxを置き換えて試す独立した例です。最初は0回と表示され、押すたびに1ずつ増えます。count + 1のように直前の状態から次を計算するときは、更新関数へ関数を渡す書き方が使えます。
import { useState } from 'react';
export default function App() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(previous => previous + 1);
}
return (
<button type="button" onClick={handleClick}>
練習した回数:{count}
</button>
);
}onClick={handleClick}はクリック時に呼ぶ関数を渡します。onClick={handleClick()}と書くと表示処理の途中で実行され、意図しない更新を招きます。また、更新関数を呼んでも、そのイベント処理で参照中のcountがその場で別の値に置き換わるわけではありません。次のレンダーで新しいstateを使った表示が作られます。
useStateなどのHooksは、関数コンポーネントやカスタムHookのトップレベルで呼び出します。条件分岐や繰り返し、クリック用の関数の中に置かないでください。複数の部品で同じ値を使うときは、共通の親にstateを置き、値と更新用の関数をpropsで渡す方法から考えます。
すべてをグローバルな状態管理へ集約する必要はありません。入力欄だけで使う値はその近くに置き、表示件数と一覧が共有するデータは親に置く、と役割で決めます。更新の種類が増えたらuseReducerの実践、離れた階層への共有が必要になったらuseContextを含む基本Hooksへ進んでください。
ここからは完成形のコードです。前の例を残したまま追記せず、src/App.jsx全体を次の内容で置き換えてください。最初は「JSXを読む」が1件表示されます。入力した学習項目を追加し、完了にチェックを入れ、不要になった項目を削除できます。データ保存や通信は実装していないため、ページを再読み込みすると初期状態に戻ります。
import { useRef, useState } from 'react';
import './App.css';
function TaskItem({ task, onToggle, onRemove }) {
return (
<li className="task-row">
<label>
<input
type="checkbox"
checked={task.done}
onChange={() => onToggle(task.id)}
/>
<span className={task.done ? 'completed' : ''}>{task.title}</span>
</label>
<button type="button" onClick={() => onRemove(task.id)}
aria-label={`${task.title}を削除`}>削除</button>
</li>
);
}
export default function App() {
const [title, setTitle] = useState('');
const [tasks, setTasks] = useState([
{ id: 1, title: 'JSXを読む', done: false },
]);
const nextId = useRef(2);
const completed = tasks.filter(task => task.done).length;
function handleSubmit(event) {
event.preventDefault();
const trimmed = title.trim();
if (!trimmed) return;
const task = { id: nextId.current++, title: trimmed, done: false };
setTasks(current => [...current, task]);
setTitle('');
}
function toggleTask(id) {
setTasks(current => current.map(task =>
task.id === id ? { ...task, done: !task.done } : task
));
}
function removeTask(id) {
setTasks(current => current.filter(task => task.id !== id));
}
return (
<main className="lesson-app">
<h1>学習チェックリスト</h1>
<form onSubmit={handleSubmit}>
<label htmlFor="task-title">次に学ぶこと</label>
<div className="input-row">
<input id="task-title" value={title} maxLength={80}
onChange={event => setTitle(event.target.value)} />
<button type="submit" disabled={!title.trim()}>追加</button>
</div>
</form>
<p role="status">{tasks.length}件中{completed}件が完了</p>
{tasks.length === 0 ? <p>学習項目を追加してください。</p> : (
<ul className="task-list">
{tasks.map(task => (
<TaskItem key={task.id} task={task}
onToggle={toggleTask} onRemove={removeTask} />
))}
</ul>
)}
</main>
);
}テンプレートの初期スタイルが混ざらないように、src/index.cssは空にし、src/App.cssを次の内容で置き換えます。src/main.jsxはそのまま使います。チェックボックスのラベルをクリックできること、キーボードのTabキーで操作部品に移動できることも確認しましょう。
:root { font-family: system-ui, sans-serif; color: #15201f; background: #f7f8f5; }
* { box-sizing: border-box; }
body { margin: 0; }
button, input { font: inherit; }
button { padding: 0.6rem 0.9rem; cursor: pointer; }
button:disabled { cursor: default; }
button:focus-visible, input:focus-visible { outline: 3px solid #2477b8; outline-offset: 3px; }
.lesson-app { max-width: 42rem; margin: 3rem auto; padding: 1rem; }
.input-row { display: flex; gap: 0.75rem; margin-top: 0.5rem; }
.input-row input { flex: 1; min-width: 0; padding: 0.6rem; }
.task-list { list-style: none; padding: 0; }
.task-row { display: flex; align-items: center; gap: 1rem; padding: 1rem 0; border-bottom: 1px solid #d8ded8; }
.task-row label { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.task-row input { margin-right: 0.6rem; }
.completed { text-decoration: line-through; }空欄や空白だけの入力では追加できません。「stateを学ぶ」を追加すると件数が2件になり、入力欄は空になります。チェックを入れると完了数が増え、文字に取り消し線が付きます。全項目を削除したら、案内文が表示されます。同じタイトルを2件追加しても、それぞれを別の項目として操作できれば、タイトルではなくIDで識別できています。
一覧はmap()で要素へ変換し、keyにはデータのIDを指定します。配列の位置やタイトルは、並べ替えや重複で不都合が起きるため、この例では生成時に決めたIDを使います。nextIdは再描画に使わない連番なのでuseRefで保持しています。このIDは1つのアプリ内だけで使う値で、保存や複数端末での共有には別途ID設計が必要です。一覧とkeyの公式解説も確認してください。
追加では配列の展開、完了の切り替えではmap()、削除ではfilter()で新しい配列を作ります。state内の配列をpush()で直接変更したり、元のオブジェクトのdoneを直接書き換えたりしない点が大切です。配列のstate更新で、追加・削除・置き換えの考え方を復習できます。
テキスト入力はvalueとonChange、チェックボックスはcheckedとonChangeを組み合わせています。画面に見えている値とstateを対応させるためです。送信時のpreventDefault()はフォーム送信によるページ移動を止めます。詳細はReactのinputリファレンスを参照してください。フォームを複数配置するときのIDはuseIdの実践記事へ進めます。
完成コードでは、完了数をtasks.filter(...).lengthで求めています。既に持っている配列から計算できるので、完了数専用のstateと、それを同期するEffectを追加する必要はありません。保存する値を増やすほど、どちらか一方の更新漏れが起きやすくなります。
クリック後の追加や削除はイベントハンドラー、現在のデータから求める件数はレンダー中の計算、と整理しましょう。こうすると「何がきっかけで動く処理か」がコードから読み取れます。Effectが不要なケースでは、この判断が詳しく説明されています。
useEffectは外部システムとの同期に使います。ブラウザーのイベント監視、タイマー、React以外のUIライブラリとの連携などでは、設定した処理を解除する後始末も検討します。依存配列は都合よく回数を抑えるために省略するものではなく、Effect内で参照する値との対応を確認します。
API通信を追加するなら、読み込み中・失敗・結果なしの表示に加え、古い通信結果で新しい状態を上書きしない仕組みも必要です。この入門例はローカルの状態管理に範囲を絞っています。通信やイベント監視へ進む前に、Effectによる同期の公式解説と基本Hooksのコード例を確認してください。
まずターミナルで、今いるフォルダーにpackage.jsonがあるか確認します。npm run devが見つからない場合は、プロジェクトの外で実行していないか、依存パッケージのインストールが終わっているかを調べます。ブラウザーだけ再読み込みしても、開発サーバーが止まっている問題は解消しません。
JSXのエラーでは、閉じタグ、括弧、returnの位置、import先のファイル名を順に確認します。ファイル名の大文字・小文字の違いは、環境を変えたときに問題になる場合があります。エラー文の最初の行と示されたファイル・行番号を読み、複数箇所を一度に変えず、1つ直したら表示を確認してください。
入力できない場合はvalueだけ指定してonChangeを忘れていないかを確認します。ボタンでは関数を渡す場所で呼び出していないか、stateを直接代入していないかを見直します。フォーム内の削除ボタンにtype="button"がないと送信として動く場合があるため、ボタンの役割を明示しましょう。
開発中にログやEffectの実行が重なって見える場合は、StrictModeの追加チェックが関係していることがあります。チェックを消す前に、レンダー中にデータを書き換えていないか、Effectの後始末があるかを確認します。StrictModeの公式説明と組み込み機能の解説で、開発時と本番の挙動を区別できます。
完成コードを見ずに書けることより、「どのstateが、どの操作で、どの表示を変えるか」を説明できることを目安にしてください。練習として、未完了だけを表示する切り替えや、完了済みをまとめて削除するボタンを追加してみましょう。表示対象を変えるだけなら元の配列を削除せず、表示用の配列を計算する方法を考えます。
部品を分ける練習には読書リストの入門記事、複数の更新操作を整理する練習にはuseReducerが使えます。動作に問題がない段階で、必要に応じてuseMemo・useCallbackやuseTransition・useDeferredValueに進みます。最適化のAPIを最初からすべて盛り込む必要はありません。
JavaScriptでpropsとstateの流れを理解した後、TypeScriptでデータの形を表す練習へ進めます。型はコードの整合性を確認する助けになりますが、UIの使いやすさや通信先から届いたデータの正しさを自動で保証するものではありません。TypeScript入門ガイドから必要な基礎を選んでください。
本や動画を使う場合は、関数コンポーネントとHooksを扱っているか、環境構築の手順が手元のバージョンに合うかを確認します。丸写しで終わらせず、入力値や初期データを変えて結果を予想すると理解を確かめられます。無料で読めるReact公式のクイックスタートは基本の復習、React公式リファレンスはAPIごとの条件や注意点の確認に使い分けましょう。