Reactは世界で最も広く使われているフロントエンドライブラリのひとつです。しかし「コンポーネントの分割粒度がわからない」「HooksのAPIが多くて混乱する」「状態管理ライブラリを何から選べばいいかわからない」といった悩みを持つ方も多いのではないでしょうか。
Antigravityを使えば、こうした学習ハードルを大幅に下げながら、実践的なReactアプリを短時間で構築できます。ここではAntigravityとReactを組み合わせた開発の基本から、Hooks・状態管理まで、具体的なコード例とともに丁寧に解説します。
なぜAntigravityでReact開発が加速するのか
ReactはJavaScriptの知識に加えて、JSX構文・コンポーネント設計・ライフサイクル・Hooksなど、習得すべき概念が多いライブラリです。Antigravityを活用することで、以下のような恩恵を受けられます。
- コンポーネントの自動生成: 「ユーザーカードコンポーネントを作って」と伝えるだけで、propsの型定義からJSXまで一気に生成
- リファクタリング提案: クラスコンポーネントを関数コンポーネント+Hooksへ自動変換
- エラーの即時解決: TypeScriptの型エラーや実行時エラーをAIが原因分析&修正提案
- ベストプラクティスの適用: useMemo・useCallback・Reactのレンダリング最適化を自動で組み込み
AntigravityのEditor ViewとReactの相性は抜群です。プロンプトを書くだけで、プロジェクトの既存コードベースを理解した上でコンポーネントを生成してくれるため、初心者でもすぐに実践的なコードが書けるようになります。
プロジェクトのセットアップ
まず、AntigravityでReactプロジェクトを作成する最もシンプルな方法を見ていきましょう。
# Vite + React + TypeScriptで新規プロジェクトを作成
npm create vite@latest my-react-app -- --template react-ts
cd my-react-app
npm install
npm run devプロジェクトを作成したら、AntigravityでフォルダをOpenします。Antigravityはプロジェクト構造を自動的に認識し、以降のコード生成の精度が格段に上がります。
次に、.antigravity/rules ファイルを作成して、プロジェクト固有のルールを設定しておくと、生成されるコードの品質がさらに向上します。
# .antigravity/rules
このプロジェクトはReact + TypeScriptで構築されています。
- 関数コンポーネントとHooksを使用してください(クラスコンポーネント禁止)
- PropsはTypeScriptのinterfaceで定義してください
- コンポーネントファイルは src/components/ に配置してください
- カスタムHooksは src/hooks/ に配置してください
- テストファイルは Vitest を使用してください
このルールファイルを設定するだけで、Antigravityが生成するコードが常にプロジェクトの規約に沿ったものになります。
コンポーネント設計の基本をAIで学ぶ
単純なコンポーネントの作成
Antigravityで最初のコンポーネントを作ってみましょう。チャット入力欄で「ユーザープロフィールカードコンポーネントを作成してください。名前・アバター画像・自己紹介文を表示します」と入力するだけで、以下のようなコードが生成されます。
// src/components/UserProfileCard.tsx
interface UserProfileCardProps {
name: string;
avatarUrl: string;
bio: string;
}
export function UserProfileCard({ name, avatarUrl, bio }: UserProfileCardProps) {
return (
<div className="flex items-center gap-4 p-4 rounded-lg border bg-white shadow-sm">
<img
src={avatarUrl}
alt={`${name}のアバター`}
className="w-16 h-16 rounded-full object-cover"
/>
<div>
<h2 className="text-lg font-semibold text-gray-900">{name}</h2>
<p className="text-sm text-gray-500 mt-1">{bio}</p>
</div>
</div>
);
}この生成されたコードのポイントは、TypeScriptのinterface定義が自動的に含まれていること、アクセシビリティのための alt 属性が設定されていること、そしてTailwind CSSのクラス名がすでに付与されていることです。
コンポーネントの分割粒度を相談する
コンポーネント設計で迷ったときも、Antigravityに相談できます。「このコンポーネントをさらに小さく分割すべきか判断してください」と聞くと、責任の分離・再利用性・可読性の観点から適切なアドバイスを得られます。
一般的な分割基準として、Antigravityはよく以下の観点を提示してくれます。
- 100行を超えてきたら分割を検討する
- ロジックとUIが混在している場合はカスタムHooksに分離
- 同じパターンが3回以上繰り返されたらコンポーネント化
React Hooksの実践的な使い方
useState — 状態管理の基本
useState はReactの最も基本的なHookです。Antigravityでは「フォームのバリデーション付き入力コンポーネントを作成して」と伝えると、useState を使った実践的な実装が得られます。
// src/components/ValidatedInput.tsx
import { useState } from "react";
interface ValidatedInputProps {
label: string;
placeholder?: string;
required?: boolean;
minLength?: number;
onValidChange: (value: string, isValid: boolean) => void;
}
export function ValidatedInput({
label,
placeholder,
required = false,
minLength = 0,
onValidChange,
}: ValidatedInputProps) {
const [value, setValue] = useState("");
const [touched, setTouched] = useState(false);
// バリデーションロジック
const errorMessage = (() => {
if (required && value.trim() === "") return "必須項目です";
if (minLength > 0 && value.length < minLength) {
return `${minLength}文字以上で入力してください`;
}
return null;
})();
const isValid = errorMessage === null;
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newValue = e.target.value;
setValue(newValue);
if (touched) {
onValidChange(newValue, isValid);
}
};
const handleBlur = () => {
setTouched(true);
onValidChange(value, isValid);
};
return (
<div className="flex flex-col gap-1">
<label className="text-sm font-medium text-gray-700">
{label}
{required && <span className="text-red-500 ml-1">*</span>}
</label>
<input
type="text"
value={value}
placeholder={placeholder}
onChange={handleChange}
onBlur={handleBlur}
className={`px-3 py-2 border rounded-md text-sm ${
touched && !isValid
? "border-red-400 focus:ring-red-300"
: "border-gray-300 focus:ring-blue-300"
} focus:outline-none focus:ring-2`}
/>
{touched && errorMessage && (
<p className="text-xs text-red-500">{errorMessage}</p>
)}
</div>
);
}このコードは、入力値・タッチ状態・バリデーション結果を管理する実践的なフォームコンポーネントです。Antigravityにこのコードを生成させた後、「エラーメッセージをアニメーション付きで表示したい」「パスワード強度チェックも追加して」など、追加要件を自然言語で伝えれば、AIが差分だけを編集してくれます。
useEffect — 副作用の管理
APIからデータをフェッチする場合など、副作用の処理には useEffect を使います。
// src/hooks/useGitHubUser.ts
import { useState, useEffect } from "react";
interface GitHubUser {
login: string;
name: string;
avatar_url: string;
bio: string;
public_repos: number;
followers: number;
}
interface UseGitHubUserResult {
user: GitHubUser | null;
loading: boolean;
error: string | null;
}
export function useGitHubUser(username: string): UseGitHubUserResult {
const [user, setUser] = useState<GitHubUser | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
if (!username) return;
let cancelled = false; // クリーンアップ用フラグ
setLoading(true);
setError(null);
fetch(`https://api.github.com/users/${username}`)
.then((res) => {
if (!res.ok) throw new Error("ユーザーが見つかりません");
return res.json();
})
.then((data: GitHubUser) => {
if (!cancelled) {
setUser(data);
setLoading(false);
}
})
.catch((err: Error) => {
if (!cancelled) {
setError(err.message);
setLoading(false);
}
});
// クリーンアップ: usernameが変わったら前のリクエストを無視
return () => {
cancelled = true;
};
}, [username]); // usernameが変わるたびに再実行
return { user, loading, error };
}このカスタムHooksは、GitHubのユーザー情報を非同期で取得し、ローディング・エラー状態もあわせて管理します。cancelled フラグによるクリーンアップ処理も自動的に含まれており、競合状態(Race Condition)を防いでいます。
AntigravityにこのようなHooksを生成させるときは、「TypeScriptで型安全にして」「エラーハンドリングを含めて」「クリーンアップ処理も入れて」と要件を明示すると、より堅牢なコードが得られます。
useReducer — 複雑な状態管理
複数の関連する状態がある場合、useReducer が useState より適していることがあります。Antigravityに「ショッピングカートの状態管理をuseReducerで実装して」と頼むと、以下のような実装が得られます。
// src/hooks/useCart.ts
import { useReducer } from "react";
interface CartItem {
id: string;
name: string;
price: number;
quantity: number;
}
interface CartState {
items: CartItem[];
total: number;
}
type CartAction =
| { type: "ADD_ITEM"; payload: Omit<CartItem, "quantity"> }
| { type: "REMOVE_ITEM"; payload: { id: string } }
| { type: "UPDATE_QUANTITY"; payload: { id: string; quantity: number } }
| { type: "CLEAR_CART" };
function cartReducer(state: CartState, action: CartAction): CartState {
switch (action.type) {
case "ADD_ITEM": {
const existing = state.items.find((item) => item.id === action.payload.id);
const newItems = existing
? state.items.map((item) =>
item.id === action.payload.id
? { ...item, quantity: item.quantity + 1 }
: item
)
: [...state.items, { ...action.payload, quantity: 1 }];
return {
items: newItems,
total: newItems.reduce((sum, item) => sum + item.price * item.quantity, 0),
};
}
case "REMOVE_ITEM": {
const newItems = state.items.filter((item) => item.id !== action.payload.id);
return {
items: newItems,
total: newItems.reduce((sum, item) => sum + item.price * item.quantity, 0),
};
}
case "UPDATE_QUANTITY": {
const newItems = state.items.map((item) =>
item.id === action.payload.id
? { ...item, quantity: Math.max(0, action.payload.quantity) }
: item
).filter((item) => item.quantity > 0);
return {
items: newItems,
total: newItems.reduce((sum, item) => sum + item.price * item.quantity, 0),
};
}
case "CLEAR_CART":
return { items: [], total: 0 };
default:
return state;
}
}
const initialState: CartState = { items: [], total: 0 };
export function useCart() {
const [state, dispatch] = useReducer(cartReducer, initialState);
return {
items: state.items,
total: state.total,
addItem: (item: Omit<CartItem, "quantity">) =>
dispatch({ type: "ADD_ITEM", payload: item }),
removeItem: (id: string) => dispatch({ type: "REMOVE_ITEM", payload: { id } }),
updateQuantity: (id: string, quantity: number) =>
dispatch({ type: "UPDATE_QUANTITY", payload: { id, quantity } }),
clearCart: () => dispatch({ type: "CLEAR_CART" }),
};
}このパターンのメリットは、状態変化のロジックがすべて cartReducer に集約されていることです。テストを書きやすく、バグの原因特定も容易になります。
React状態管理のさらに深い実装については、Antigravity × Storybook でコンポーネント駆動開発を加速するでもコンポーネントのテスト手法と合わせて解説しています。
Context APIで状態を共有する
複数のコンポーネント間で状態を共有したい場合、Props Drillingを避けるためにContext APIを活用します。
// src/context/ThemeContext.tsx
import { createContext, useContext, useState, ReactNode } from "react";
type Theme = "light" | "dark";
interface ThemeContextValue {
theme: Theme;
toggleTheme: () => void;
}
const ThemeContext = createContext<ThemeContextValue | undefined>(undefined);
export function ThemeProvider({ children }: { children: ReactNode }) {
const [theme, setTheme] = useState<Theme>("light");
const toggleTheme = () => {
setTheme((prev) => (prev === "light" ? "dark" : "light"));
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
<div className={theme === "dark" ? "dark" : ""}>{children}</div>
</ThemeContext.Provider>
);
}
// カスタムHooksでContextを安全に使う
export function useTheme(): ThemeContextValue {
const context = useContext(ThemeContext);
if (!context) {
throw new Error("useTheme は ThemeProvider の内部で使用してください");
}
return context;
}Antigravityにこのパターンを生成させるとき、「ThemeProviderの外でuseThemeを使ったときのエラーハンドリングも含めて」と一言添えるだけで、安全なカスタムHooksが自動生成されます。
よくあるエラーと対処法
Reactを使っていると必ずといっていいほど遭遇するエラーをまとめましました。これらはAntigravityのチャットに貼り付けるだけで、原因と修正方法を即座に教えてくれます。
エラー1: Warning: Each child in a list should have a unique "key" prop
リストのレンダリングで key プロパティが不足している場合に発生します。AntigravityはMapで生成するリストに key を自動的に追加しますが、手書きコードでは忘れがちです。key には配列のインデックスより、データの一意なIDを使うことを推奨します。
エラー2: Cannot update a component while rendering a different component
レンダリング中に別コンポーネントのstateを更新しようとすると発生します。useEffect の中で状態更新するか、コールバック経由で親に通知する設計に変更することで解決できます。
エラー3: Too many re-renders
useState や useEffect の依存配列の設定ミスで無限レンダリングが発生します。Antigravityに「この useEffect の依存配列が正しいか確認して」と伝えると、ESLintルール exhaustive-deps に準拠した正しい依存配列を提案してくれます。
Antigravityのチャット欄にエラーメッセージをそのままペーストし、「このエラーの原因と修正方法を教えてください」と尋ねるだけで、多くのケースで即解決できます。詳しいデバッグ手法については、Antigravity AI デバッグ活用ガイドを参考にしてください。
コンポーネントのパフォーマンス最適化
アプリが大きくなると、不要な再レンダリングがパフォーマンスに影響してきます。Antigravityに「このコンポーネントのパフォーマンスを最適化して」と伝えると、以下のような改善を自動的に提案してくれます。
// 最適化前
function ExpensiveList({ items, onSelect }: Props) {
// itemsが変わるたびにフィルタリングが再実行される
const filtered = items.filter((item) => item.active);
// 関数が毎回新しく作られる
const handleSelect = (id: string) => {
onSelect(id);
};
return (
<ul>
{filtered.map((item) => (
<ListItem key={item.id} item={item} onSelect={handleSelect} />
))}
</ul>
);
}
// 最適化後(Antigravityが自動で提案)
import { useMemo, useCallback, memo } from "react";
const ListItem = memo(function ListItem({
item,
onSelect,
}: {
item: Item;
onSelect: (id: string) => void;
}) {
return (
<li onClick={() => onSelect(item.id)} className="cursor-pointer py-2">
{item.name}
</li>
);
});
function ExpensiveList({ items, onSelect }: Props) {
// itemsが変わったときだけフィルタリングを再実行
const filtered = useMemo(
() => items.filter((item) => item.active),
[items]
);
// onSelectが変わったときだけ関数を再生成
const handleSelect = useCallback(
(id: string) => {
onSelect(id);
},
[onSelect]
);
return (
<ul>
{filtered.map((item) => (
<ListItem key={item.id} item={item} onSelect={handleSelect} />
))}
</ul>
);
}memo・useMemo・useCallback の使い分けもAntigravityに聞けば、コンテキストに合わせた使用場面を具体的に説明してくれます。
EditorのAI機能をフル活用する方法については、Google Antigravity Editor View 入門 — AI搭載コードエディタの完全ガイドでより詳しく解説しています。
全体を振り返って
AntigravityとReactを組み合わせることで、コンポーネント設計・Hooks・状態管理といったReactの主要概念を実践しながら素早く習得できます。ここでは以下のポイントを解説しました。
- Antigravityでのプロジェクトセットアップと
.antigravity/rulesの設定 useState・useEffect・useReducerの実践的な使い方- Context APIを使ったグローバル状態管理
memo・useMemo・useCallbackによるパフォーマンス最適化
Antigravityはコードを生成するだけでなく、「なぜこの実装が良いのか」を説明してくれる優れた学習ツールでもあります。コードに迷ったら気軽にチャットで質問してみてください。
React開発をさらに深めたい方には、Antigravity × Storybook でコンポーネント駆動開発を加速するもあわせておすすめです。コンポーネントの分離・カタログ化・テスト自動化まで体系的に学べます。また、AIコード編集の効率をさらに上げたい方は、Antigravity インラインチャット(Cmd+I)完全活用ガイドもぜひご参照ください。
なお、AntigravityのEditor Viewをより深く使いこなすヒントは、Antigravity Editor 上級カスタマイズガイドでプロ向けの設定まで網羅されています。
本記事の内容