Что такое React Server Components (RSC)?

👨‍💻 Frontend Developer 🟠 Может встретиться 🎚️ Сложный
#React #Next.js #SSR

Краткий ответ

React Server Components (RSC) — это новая архитектура React, которая позволяет компонентам выполняться на сервере:

  1. Серверные компоненты — выполняются на сервере 🖥️
  2. Клиентские компоненты — выполняются в браузере 🌐
  3. Нулевой JavaScript — серверные компоненты не отправляют JS в браузер 📦
  4. Прямой доступ к данным — могут обращаться к БД напрямую 🗄️
  5. Автоматическое разделение кода — оптимизация бандла ⚡
  6. Стриминг — постепенная загрузка контента 🌊
// Серверный компонент
async function ServerComponent() {
  const data = await fetch('api/data');
  return <div>{data.title}</div>;
}
 
// Клиентский компонент
'use client';
function ClientComponent() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

Полный ответ

React Server Components — это революционная архитектура, которая меняет способ создания React приложений! Они позволяют выполнять компоненты на сервере, что даёт множество преимуществ. 🚀

Основные концепции

1. Серверные компоненты

Выполняются на сервере и отправляют готовый HTML:

// app/posts/page.js
async function PostsPage() {
  // Выполняется на сервере
  const posts = await db.posts.findMany();
  
  return (
    <div>
      <h1>Посты</h1>
      {posts.map(post => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.content}</p>
        </article>
      ))}
    </div>
  );
}

2. Клиентские компоненты

Выполняются в браузере с интерактивностью:

// components/LikeButton.js
'use client';
import { useState } from 'react';
 
function LikeButton({ postId }) {
  const [liked, setLiked] = useState(false);
  
  return (
    <button 
      onClick={() => setLiked(!liked)}
      className={liked ? 'liked' : ''}
    >
      {liked ? '❤️' : '🤍'} Нравится
    </button>
  );
}

Архитектура RSC

// Серверный компонент (по умолчанию)
async function BlogPost({ id }) {
  const post = await getPost(id);
  
  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
      
      {/* Клиентский компонент для интерактивности */}
      <LikeButton postId={id} />
      <Comments postId={id} />
    </article>
  );
}
 
// Клиентский компонент
'use client';
function Comments({ postId }) {
  const [comments, setComments] = useState([]);
  
  useEffect(() => {
    loadComments(postId).then(setComments);
  }, [postId]);
  
  return (
    <div>
      {comments.map(comment => (
        <div key={comment.id}>{comment.text}</div>
      ))}
    </div>
  );
}

Преимущества RSC

1. Производительность

// Серверный компонент - 0 KB JavaScript
function ProductList() {
  const products = await db.products.findMany();
  return (
    <div>
      {products.map(product => (
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  );
}
 
// Только интерактивные части отправляются в браузер
'use client';
function AddToCart({ productId }) {
  return (
    <button onClick={() => addToCart(productId)}>
      В корзину
    </button>
  );
}

2. Безопасность

// Серверный компонент - секретные ключи остаются на сервере
async function UserProfile({ userId }) {
  const user = await db.users.findUnique({
    where: { id: userId },
    select: { name: true, email: true } // Не отправляем пароль
  });
  
  return <div>Привет, {user.name}!</div>;
}

3. SEO и доступность

// Серверный компонент - готовый HTML для поисковиков
async function ArticlePage({ slug }) {
  const article = await getArticle(slug);
  
  return (
    <>
      <Head>
        <title>{article.title}</title>
        <meta name="description" content={article.excerpt} />
      </Head>
      <article>
        <h1>{article.title}</h1>
        <div dangerouslySetInnerHTML={{ __html: article.content }} />
      </article>
    </>
  );
}

Стриминг и Suspense

// app/dashboard/page.js
import { Suspense } from 'react';
 
function Dashboard() {
  return (
    <div>
      <h1>Панель управления</h1>
      
      <Suspense fallback={<div>Загрузка статистики...</div>}>
        <Stats />
      </Suspense>
      
      <Suspense fallback={<div>Загрузка графиков...</div>}>
        <Charts />
      </Suspense>
    </div>
  );
}
 
// Медленный серверный компонент
async function Stats() {
  const stats = await getStats(); // Медленный запрос
  return <div>Статистика: {stats.total}</div>;
}

Передача данных между компонентами

// Серверный компонент передаёт данные клиентскому
async function PostPage({ params }) {
  const post = await getPost(params.id);
  const user = await getCurrentUser();
  
  return (
    <div>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
      
      {/* Передаём данные в клиентский компонент */}
      <InteractiveSection 
        postId={post.id}
        userId={user.id}
        initialLikes={post.likes}
      />
    </div>
  );
}
 
'use client';
function InteractiveSection({ postId, userId, initialLikes }) {
  const [likes, setLikes] = useState(initialLikes);
  
  return (
    <div>
      <button onClick={() => setLikes(likes + 1)}>
        👍 {likes}
      </button>
    </div>
  );
}

Ограничения RSC

Серверные компоненты НЕ могут:

// ❌ Использовать хуки состояния
function ServerComponent() {
  const [state, setState] = useState(0); // Ошибка!
  return <div>{state}</div>;
}
 
// ❌ Использовать эффекты
function ServerComponent() {
  useEffect(() => {}, []); // Ошибка!
  return <div>Content</div>;
}
 
// ❌ Использовать браузерные API
function ServerComponent() {
  const width = window.innerWidth; // Ошибка!
  return <div>Width: {width}</div>;
}

Клиентские компоненты НЕ могут:

// ❌ Импортировать серверные модули
'use client';
import { db } from './database'; // Ошибка!
 
function ClientComponent() {
  return <div>Client</div>;
}

Лучшие практики

  1. Используйте серверные компоненты по умолчанию 🖥️
  2. Клиентские компоненты только для интерактивности 🎯
  3. Передавайте данные через пропсы 📤
  4. Используйте Suspense для загрузки
// ✅ Правильная архитектура
async function ProductPage({ id }) {
  const product = await getProduct(id); // Серверный
  
  return (
    <div>
      <ProductInfo product={product} /> {/* Серверный */}
      <AddToCartButton productId={id} /> {/* Клиентский */}
    </div>
  );
}

Частые ошибки

Неправильно:

// Смешивание серверного и клиентского кода
function Component() {
  const [state, setState] = useState(0);
  const data = await fetch('/api'); // Ошибка!
  return <div>{data}</div>;
}

Правильно:

// Разделение ответственности
async function ServerComponent() {
  const data = await fetch('/api');
  return <ClientComponent initialData={data} />;
}
 
'use client';
function ClientComponent({ initialData }) {
  const [state, setState] = useState(0);
  return <div>{initialData.title}</div>;
}

Заключение

React Server Components — это будущее React разработки:

  • Лучшая производительность — меньше JavaScript в браузере
  • Улучшенное SEO — готовый HTML контент
  • Безопасность — серверная логика остаётся на сервере
  • Стриминг — быстрая загрузка контента

Используйте RSC для создания быстрых и эффективных приложений! 🚀