React
コンポーネントベースの UI ライブラリで、宣言的な記法と仮想 DOM の差分計算で UI の更新を扱う
React とは
React は、Meta (旧 Facebook) が 2013 年にオープンソース化した UI ライブラリで、コンポーネントベースのアーキテクチャと宣言的な記法で UI を構築する。2026 年 8 月時点の最新安定版は 19.2 系である。
宣言的な記法と差分更新
宣言的とは、DOM をどう書き換えるかではなく、ある状態のときに画面がどう見えるかを書くという意味だ。状態が変わると React はコンポーネントを呼び直し、前回の描画結果と今回の結果を突き合わせて、差があるノードだけを必要最小限の操作で書き換える。この突き合わせの対象になるメモリ上のツリーが仮想 DOM と呼ばれる。
ここを「仮想 DOM だから速い」と覚えると判断を誤る。差分の計算自体は追加の仕事であり、必要な DOM 操作を人が最小限に書き切った場合より速くなる保証はない。得られるのは、状態が増えても更新経路を人が管理しなくて済む書きやすさと、更新漏れによる表示の食い違いが起きにくいことである。実際に描画が重い原因は差分計算より、状態を置く位置が上すぎて無関係な範囲まで描き直していることの方が多い。
コンポーネント
コンポーネントは props を引数に受け取り、描画結果を返す関数として書く。同じ props なら同じ結果を返す純粋な関数にしておくことが前提で、渡された props を書き換えたり描画の途中で外部の変数を更新すると、React が描画を捨てて呼び直したときに結果が変わって破綻する。
// 関数コンポーネント (現在の標準)
function UserCard({ name, email }: { name: string; email: string }) {
return (
<div className="card">
<h2>{name}</h2>
<p>{email}</p>
</div>
);
}
// 使用
<UserCard name="Alice" email="alice@example.com" />
Hooks
Hooks は 2019 年 2 月に公開された React 16.8 で導入された仕組みで、関数コンポーネントに状態管理と副作用を持たせる。それ以前は、状態を持つコンポーネントはクラスとして書く必要があった。
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]); // count が変わるたびに実行
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
| Hook | 用途 |
|---|---|
useState | 状態管理 |
useEffect | 副作用 (API 呼び出し、DOM 操作) |
useRef | DOM 参照、値の保持 (再レンダリングなし) |
useMemo | 計算結果のメモ化 |
useCallback | 関数のメモ化 |
useContext | コンテキストの参照 |
Server Components (React 19)
Server Components は、バンドルより前に、クライアントや SSR 用のサーバーとは別の環境で描画されるコンポーネントである。動かす場は 2 通りあり、CI 上でビルド時に 1 回だけ実行する形と、リクエストごとに Web サーバーで実行する形のどちらも取れる。React 19 で安定版になった (ただしバンドラーやフレームワークを実装する側が触る下位 API は、19.x の minor 間で変わり得ると明示されている)。
// app/user-list.tsx (Server Component): 既定はこちら
async function UserList() {
const users = await db.users.findMany(); // サーバーで直接 DB アクセス
return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}
// app/search-box.tsx (Client Component)
'use client'; // ファイルの先頭に置く (関数の直前に書いても効かない)
function SearchBox() {
const [query, setQuery] = useState('');
return <input value={query} onChange={e => setQuery(e.target.value)} />;
}
境界の単位はファイルである。'use client' はそのモジュールと、そこから読み込んでいるモジュール群をまとめてクライアント側の扱いにする。入力欄 1 つのために置いた宣言が、同じファイルが読み込んでいるライブラリごとクライアントへ送られることになるため、対話が必要な部分は小さいファイルへ切り出す。逆に Server Component 側は DB 接続や鍵をそのまま扱える代わりに、状態や onClick のようなブラウザ側の仕組みを持てない。
Vue / Svelte との比較
選定で効くのは機能差より「誰が決めるか」の違いだ。React は状態管理・ルーティング・データ取得を外部の選択に任せるため、決める自由と決め続ける負担が同時に付いてくる。Vue と Svelte は公式が用意した道筋が太く、判断の回数を減らせる。
| 観点 | React | Vue | Svelte |
|---|---|---|---|
| アプローチ | ライブラリ (自由度高い) | フレームワーク (規約あり) | コンパイラ |
| テンプレート | JSX | SFC (template + script) | SFC |
| 状態管理 | Hooks, Zustand, Jotai | Composition API, Pinia | $: リアクティブ宣言 |
| 周辺ライブラリ | 選択肢が多く選定の判断が残る | 公式系 (Pinia 等) で揃う | 公式系 (SvelteKit) 中心 |
| 学習コスト | 中程度 | 低い | 低い |
Next.js との関係
React 単体はルーティング、SSR、ビルド設定を提供しない。Next.js がこれらを統合し、React アプリケーションのフルスタックフレームワークとして機能する。React の公式ドキュメントは新規アプリでフレームワークから始めることを勧めており、その一覧に App Router 版の Next.js と React Router を挙げている (2026 年 8 月時点)。Next.js が唯一の正解として指定されているわけではない。
落とし穴
再レンダリングを止めようとして useMemo と useCallback を撒くのは、たいてい逆効果になる。どちらも比較と保持のコストを払う仕組みで、依存配列を 1 つ書き損ねれば古い値を掴んだままになる。先に見るのは状態を置く位置で、変わる値を上位に置いたために無関係な範囲まで描き直していないかを確認する方が効く。
リストの key にインデックスを渡すのも定番の事故だ。並び替えや先頭への挿入が起きると、同じインデックスに別の要素が来る。React はそれを同じ要素の更新と解釈するため、入力欄の値やスクロール位置が別の行に付いて回る。key には要素自身が持つ識別子を渡す。
この記事は役に立ちましたか?
関連用語
Next.js
React ベースのフルスタックフレームワークで、SSR、SSG、API Routes を統合的に提供する
JSX
JavaScript 内に HTML ライクな構文を記述し、UI コンポーネントを宣言的に定義する React の拡張構文
TypeScript
JavaScript に静的型付けを追加した言語で、大規模開発の安全性と生産性を向上させる
Hooks
React の関数コンポーネントに状態管理や副作用などの機能を追加する仕組み
状態管理
フロントエンドアプリケーションのデータの流れと状態の変更を一元的に管理する手法
仮想 DOM
実際の DOM の軽量なコピーをメモリ上に保持し、差分だけを効率的に更新する仕組み