Astro
React
フロントエンド

AstroでReactを使う方法

AstroプロジェクトでReactコンポーネントを効果的に使用する方法を、実例とともに解説します。

著者: Tech Blog 編集部
2024-11-19
7分

なぜAstroとReact?

Astroは複数のUIフレームワークをサポートしていますが、その中でもReactは最も人気があります。既存のReactコンポーネントを再利用できるのは大きなメリットです。

セットアップ

1. Reactインテグレーションの追加

npx astro add react

このコマンドで以下が自動的に設定されます:

  • React関連パッケージのインストール
  • astro.config.mjsの更新
  • tsconfig.jsonの設定

2. 手動セットアップ

自動セットアップが使えない場合は、手動で設定できます:

npm install @astrojs/react react react-dom
// astro.config.mjs
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';

export default defineConfig({
  integrations: [react()],
});

Reactコンポーネントの作成

インタラクティブなコンポーネント

// src/components/SearchBox.tsx
import { useState } from 'react';

interface SearchBoxProps {
  onSearch: (query: string) => void;
}

export function SearchBox({ onSearch }: SearchBoxProps) {
  const [query, setQuery] = useState('');

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    onSearch(query);
  };

  return (
    <form onSubmit={handleSubmit} className="flex gap-2">
      <input
        type="text"
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="検索..."
        className="flex-1 px-4 py-2 border rounded"
      />
      <button type="submit" className="px-6 py-2 bg-blue-500 text-white rounded">
        検索
      </button>
    </form>
  );
}

Astroページでの使用

---
// src/pages/search.astro
import Layout from '../layouts/Layout.astro';
import { SearchBox } from '../components/SearchBox';
---

<Layout title="検索">
  <div class="container mx-auto p-8">
    <h1>記事を検索</h1>

    <!-- client:loadで即座にハイドレーション -->
    <SearchBox client:load onSearch={(q) => console.log(q)} />
  </div>
</Layout>

クライアントディレクティブの使い分け

client:load

ページロード直後に必要な重要なコンポーネント(ナビゲーションメニューなど)に使用:

<Navigation client:load />

client:idle

優先度の低いコンポーネント(チャットウィジェット、広告など)に使用:

<ChatWidget client:idle />

client:visible

スクロールして表示されるコンポーネント(コメント欄、画像ギャラリーなど)に使用:

<CommentSection client:visible />

client:media

レスポンシブなコンポーネント(モバイルメニューなど)に使用:

<MobileMenu client:media="(max-width: 768px)" />

Propsの受け渡し

静的Props

---
const title = "記事タイトル";
const tags = ["React", "Astro"];
---

<ArticleCard
  client:idle
  title={title}
  tags={tags}
/>

関数Props(注意点)

関数をpropsとして渡す場合は、そのコンポーネントをハイドレーションする必要があります:

<!-- ❌ これは動作しません -->
<Button onClick={() => console.log('click')} />

<!-- ✅ client:*ディレクティブが必要 -->
<Button client:idle onClick={() => console.log('click')} />

状態管理

ローカル状態

単純な状態はuseStateで管理:

import { useState } from 'react';

export function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(count + 1)}>
      カウント: {count}
    </button>
  );
}

グローバル状態

複数のコンポーネント間で状態を共有する場合は、Zustandやnanostoresを使用:

// store.ts
import { atom } from 'nanostores';

export const $cartItems = atom<string[]>([]);
// CartButton.tsx
import { useStore } from '@nanostores/react';
import { $cartItems } from '../store';

export function CartButton() {
  const items = useStore($cartItems);
  return <button>カート ({items.length})</button>;
}

パフォーマンス最適化

1. コンポーネントの遅延ロード

---
// 重いコンポーネントは遅延ロード
---

<HeavyChart client:visible />

2. 静的レンダリングの活用

インタラクティブでない部分はAstroコンポーネントで:

<!-- Astroコンポーネント(静的) -->
<article class="prose">
  <h1>{post.title}</h1>
  <div set:html={post.content} />
</article>

<!-- Reactコンポーネント(インタラクティブ) -->
<LikeButton client:visible postId={post.id} />

まとめ

AstroとReactの組み合わせにより:

  • 既存のReactコンポーネントを再利用
  • 必要な部分だけJSを配信
  • 高速なページロードを実現
  • 優れたSEOを維持

アイランドアーキテクチャの思想を理解して、適切にクライアントディレクティブを使い分けることが重要です。