React + TypeScript 実践ベストプラクティス2026:プロが使うパターン集
2026年のReact + TypeScript開発の現状
React 19 + TypeScript 5.x の組み合わせは、2026年時点でフロントエンド開発の標準スタックとなっています。
採用企業のトレンド(2026年求人分析):
- React必須:フロントエンド求人の約70%
- TypeScript必須:フロントエンド求人の約65%
- Next.js追加要件:フロントエンド求人の約50%
ベストプラクティス1:型定義の設計
避けるべきパターン
// ❌ 悪い例:anyを使う
function processData(data: any) {
return data.map((item: any) => item.name);
}
// ❌ 悪い例:型アサーション乱用
const user = response.data as User;
推奨パターン
// ✅ 良い例:型推論を活用
interface ApiResponse<T> {
data: T;
status: 'success' | 'error';
message?: string;
}
interface User {
id: number;
name: string;
email: string;
role: 'admin' | 'user' | 'viewer';
}
async function fetchUsers(): Promise<ApiResponse<User[]>> {
const response = await fetch('/api/users');
return response.json();
}
// ✅ 型ガードで安全に絞り込む
function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'name' in value
);
}
ベストプラクティス2:カスタムフック設計
// ✅ 汎用的なデータフェッチフック
interface FetchState<T> {
data: T | null;
loading: boolean;
error: Error | null;
}
function useFetch<T>(url: string): FetchState<T> {
const [state, setState] = useState<FetchState<T>>({
data: null,
loading: true,
error: null,
});
useEffect(() => {
let cancelled = false;
const fetchData = async () => {
try {
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP error: ${response.status}`);
const data: T = await response.json();
if (!cancelled) setState({ data, loading: false, error: null });
} catch (error) {
if (!cancelled) setState({
data: null,
loading: false,
error: error instanceof Error ? error : new Error('Unknown error'),
});
}
};
fetchData();
return () => { cancelled = true; };
}, [url]);
return state;
}
// 使用例
function UserList() {
const { data: users, loading, error } = useFetch<User[]>('/api/users');
if (loading) return <div>読み込み中...</div>;
if (error) return <div>エラー: {error.message}</div>;
if (!users) return null;
return <ul>{users.map(user => <li key={user.id}>{user.name}</li>)}</ul>;
}
ベストプラクティス3:React Server Components(Next.js 15)
// ✅ Server Componentでデータフェッチ(パフォーマンス最適化)
// app/users/page.tsx
async function UsersPage() {
// サーバーサイドで実行(バンドルサイズに影響しない)
const users = await db.user.findMany({
orderBy: { createdAt: 'desc' },
take: 20,
});
return (
<div>
<h1>ユーザー一覧</h1>
{users.map(user => (
<UserCard key={user.id} user={user} />
))}
</div>
);
}
// ✅ 'use client'はインタラクティブな部分だけに
'use client';
function UserCard({ user }: { user: User }) {
const [expanded, setExpanded] = useState(false);
return (
<div onClick={() => setExpanded(!expanded)}>
<h2>{user.name}</h2>
{expanded && <p>{user.email}</p>}
</div>
);
}
ベストプラクティス4:パフォーマンス最適化
// ✅ useMemoとuseCallbackの適切な使用
function ExpensiveList({ items, onSelect }: {
items: Item[];
onSelect: (id: number) => void;
}) {
// 重い計算はuseMemoでメモ化
const sortedItems = useMemo(
() => [...items].sort((a, b) => a.priority - b.priority),
[items] // itemsが変わった時だけ再計算
);
// コールバックはuseCallbackでメモ化(子コンポーネントへの影響を防ぐ)
const handleSelect = useCallback(
(id: number) => onSelect(id),
[onSelect]
);
return (
<ul>
{sortedItems.map(item => (
<MemoizedItem
key={item.id}
item={item}
onSelect={handleSelect}
/>
))}
</ul>
);
}
// ✅ React.memoで不要な再レンダリングを防ぐ
const MemoizedItem = memo(function Item({
item,
onSelect,
}: {
item: Item;
onSelect: (id: number) => void;
}) {
return <li onClick={() => onSelect(item.id)}>{item.name}</li>;
});
ベストプラクティス5:テスト戦略
// ✅ Vitestを使ったコンポーネントテスト
import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import { UserCard } from './UserCard';
describe('UserCard', () => {
const mockUser: User = {
id: 1,
name: 'テスト太郎',
email: 'test@example.com',
role: 'user',
};
it('ユーザー名が表示される', () => {
render(<UserCard user={mockUser} />);
expect(screen.getByText('テスト太郎')).toBeInTheDocument();
});
it('クリックで詳細が展開される', () => {
render(<UserCard user={mockUser} />);
const card = screen.getByRole('button');
fireEvent.click(card);
expect(screen.getByText('test@example.com')).toBeInTheDocument();
});
});
Vitestの詳しい使い方はVitest完全ガイド2026をご覧ください。
AIを活用したReact開発
2026年ではAIコーディングアシスタントとReact開発を組み合わせることが一般化しています。
# Claude Codeでコンポーネントを自動生成する例
claude
> "以下の要件でTypeScriptのReactコンポーネントを作ってください:
> - ユーザー一覧をAPIから取得して表示する
> - ローディング・エラー状態を適切に処理する
> - テストコードも一緒に作ってください"
AIツールを効果的に活用する方法はClaude Code完全ガイド2026で詳しく解説しています。
ベストプラクティス6:カスタムフックの実践パターン
実際のプロジェクトでよく使う高度なカスタムフックのパターンを紹介します。
useLocalStorage — 型安全なローカルストレージ
useState のインターフェースでLocalStorageを操作するフックです。ジェネリクスで型を指定でき、SSR環境(typeof window === 'undefined')にも対応します。値の変更時に自動的にLocalStorageへ保存され、初期値はLocalStorageの既存値があればそちらを使います。
ベストプラクティス7:Error Boundaryの型安全な実装
React 19でもError Boundaryはクラスコンポーネントが必要ですが、TypeScriptで型安全に実装できます。
class ErrorBoundary extends React.Component<
{ children: React.ReactNode; fallback: React.ReactNode | ((error: Error) => React.ReactNode) },
{ hasError: boolean; error: Error | null }
> {
state = { hasError: false, error: null as Error | null };
static getDerivedStateFromError(error: Error) {
return { hasError: true, error };
}
render() {
if (this.state.hasError && this.state.error) {
const { fallback } = this.props;
return typeof fallback === 'function' ? fallback(this.state.error) : fallback;
}
return this.props.children;
}
}
ベストプラクティス8:useTransitionによるUI応答性の改善
React 19の useTransition を使うと、重い状態更新をバックグラウンドで実行し、UIの応答性を維持できます。
function FilterableList({ items }: { items: Item[] }) {
const [query, setQuery] = useState('');
const [isPending, startTransition] = useTransition();
const [filteredItems, setFilteredItems] = useState(items);
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
setQuery(e.target.value); // 即座に入力欄を更新
startTransition(() => {
setFilteredItems(items.filter(item =>
item.name.toLowerCase().includes(e.target.value.toLowerCase())
));
});
};
return (
<div>
<input value={query} onChange={handleSearch} placeholder="検索..." />
{isPending && <span>フィルタリング中...</span>}
<ul>{filteredItems.map(item => <li key={item.id}>{item.name}</li>)}</ul>
</div>
);
}
関連記事
-
Vitest完全ガイド2026 — モダンなテスト環境の構築
-
Next.js 15完全ガイド2026 — App RouterとServer Actionsの全機能
-
Claude Code完全ガイド2026 — AIでReact開発を10倍速に
-
AIコーディングツール比較2026 — Cursor vs Windsurf vs Copilot
-
エンジニア年収を上げる戦略2026 — TypeScriptスキルで年収アップ