8. React в Astro
Интеграция React в Astro позволяет использовать мощь React-компонентов внутри быстрых статических страниц. Благодаря архитектуре Islands вы получаете лучшее из двух миров: скорость статического HTML и интерактивность React там, где она действительно нужна.
Установка интеграции
Заголовок раздела «Установка интеграции»npx astro add reactКоманда автоматически:
- Устанавливает
@astrojs/react,react,react-dom - Обновляет
astro.config.mjs - Настраивает TypeScript типы (при наличии TS)
Конфигурация astro.config.mjs
Заголовок раздела «Конфигурация astro.config.mjs»import { defineConfig } from 'astro/config';import react from '@astrojs/react';
export default defineConfig({ integrations: [react()],});Использование React компонентов в .astro файлах
Заголовок раздела «Использование React компонентов в .astro файлах»---import Counter from '../components/Counter.jsx';import LikeButton from '../components/LikeButton.jsx';---
<html> <body> <!-- Статический контент Astro (нет JS) --> <h1>Мой блог</h1> <p>Здесь контент без единой строки JS...</p>
<!-- React Islands — добавляют интерактивность --> <Counter client:load /> <LikeButton client:visible /> </body></html>Создание React компонента
Заголовок раздела «Создание React компонента»import { useState } from 'react';
export default function Counter({ initialCount = 0 }) { const [count, setCount] = useState(initialCount);
return ( <div className="counter"> <button onClick={() => setCount(c => c - 1)}>−</button> <span>{count}</span> <button onClick={() => setCount(c => c + 1)}>+</button> </div> );}Передача данных из Astro в React
Заголовок раздела «Передача данных из Astro в React»Props передаются как обычные атрибуты. Astro сериализует их в JSON при SSR.
---import PostActions from '../components/PostActions.jsx';
const post = await getPost(Astro.params.slug);---
<PostActions client:load postId={post.id} initialLikes={post.likes} title={post.title} isBookmarked={post.bookmarked}/>import { useState } from 'react';
export default function PostActions({ postId, initialLikes, title, isBookmarked }) { const [likes, setLikes] = useState(initialLikes); const [bookmarked, setBookmarked] = useState(isBookmarked);
const like = () => { setLikes(l => l + 1); fetch('/api/like', { method: 'POST', body: JSON.stringify({ postId }) }); };
return ( <div> <button onClick={like}>❤️ {likes}</button> <button onClick={() => setBookmarked(b => !b)}> {bookmarked ? '🔖 Сохранено' : '📌 Сохранить'} </button> </div> );}React Hooks в Astro Islands
Заголовок раздела «React Hooks в Astro Islands»Все React хуки работают как обычно внутри Islands:
import { useState, useEffect } from 'react';
export default function ThemeToggle() { const [theme, setTheme] = useState('light');
useEffect(() => { const saved = localStorage.getItem('theme') || 'light'; setTheme(saved); document.documentElement.setAttribute('data-theme', saved); }, []);
const toggle = () => { const next = theme === 'light' ? 'dark' : 'light'; setTheme(next); localStorage.setItem('theme', next); document.documentElement.setAttribute('data-theme', next); };
return ( <button onClick={toggle} aria-label="Переключить тему"> {theme === 'light' ? '🌙' : '☀️'} </button> );}Смешивание Astro и React компонентов
Заголовок раздела «Смешивание Astro и React компонентов»---import Header from '../components/Header.astro'; // Astro — 0 JSimport Sidebar from '../components/Sidebar.astro'; // Astro — 0 JSimport SearchBar from '../components/SearchBar.jsx'; // React Islandimport Comments from '../components/Comments.jsx'; // React Island---
<Layout> <Header /> <SearchBar client:load /> <main> <slot /> <Comments client:visible postId={post.id} /> </main> <Sidebar /></Layout>Оптимизация: несколько Islands
Заголовок раздела «Оптимизация: несколько Islands»Каждый React Island является независимым и изолированным. Состояние не разделяется между ними автоматически — используйте nanostores или Context через общий стор для коммуникации.
// src/store/cart.js — nanostores для общего стейтаimport { atom } from 'nanostores';export const cartCount = atom(0);// CartIcon.jsx — реагирует на общий сторimport { useStore } from '@nanostores/react';import { cartCount } from '../store/cart';
export default function CartIcon() { const count = useStore(cartCount); return <span>🛒 {count}</span>;}