JavaRush /Курсы /Модуль 4: Node.js, Next.js и Angular /Client Components: когда и зачем применять,

Client Components: когда и зачем применять, "use client"

Модуль 4: Node.js, Next.js и Angular
7 уровень , 7 лекция
Открыта

1. Введение в Client Components

Если Server Components — это такие «тихие интроверты», которые любят работать на сервере и не лезут в браузер, то Client Components — настоящие «души компании», которые живут в браузере, слушают пользователя и делают приложение интерактивным.

Client Component — это React-компонент, который выполняется на клиенте (в браузере пользователя). Такой компонент может использовать React-хуки (useState, useEffect и т.д.), напрямую работать с DOM, слушать события и, конечно же, обновлять интерфейс в ответ на действия пользователя.

В Next.js 13+ (и особенно в App Router) все компоненты по умолчанию считаются Server Components. Чтобы сделать компонент клиентским, нужно явно это указать.

Как объявить Client Component?

Очень просто: на самой первой строке файла компонента пишется директива "use client" (строго в кавычках, без запятых, без export, без скобок):


"use client";

import { useState } from "react";

export default function Counter() {
  const [count, setCount] = useState(0);

  return (
    <button onClick={() => setCount(count + 1)}>
      Кликнули {count} раз
    </button>
  );
}

Важно: Директива "use client" должна стоять первой строкой в файле. Если добавить её ниже — Next.js просто не поймёт, что вы хотели.

2. Когда нужны Client Components?

В мире Server Components всё хорошо, пока вам не понадобится интерактивность. Но как только появляется задача реагировать на действия пользователя — без Client Components не обойтись.

Примеры ситуаций, когда нужны Client Components:

  • Кнопка, которая увеличивает счётчик (useState)
  • Форма с валидацией на клиенте
  • Выпадающее меню, модальное окно, табы
  • Анимации, которые зависят от действий пользователя
  • Интеграция с браузерными API (например, localStorage, window, document)
  • Любые сторонние React-библиотеки, которые требуют хуков или эффектов (например, react-select, react-datepicker и т.д.)

Пример: простой счётчик


"use client";
import { useState } from "react";

export default function Counter() {
  const [value, setValue] = useState(0);

  return (
    <div>
      <p>Значение: {value}</p>
      <button onClick={() => setValue(value + 1)}>+1</button>
    </div>
  );
}

Пример: форма с валидацией


"use client";
import { useState } from "react";

export default function SimpleForm() {
  const [name, setName] = useState("");
  const [error, setError] = useState("");

  const handleSubmit = (e) => {
    e.preventDefault();
    if (name.length < 3) {
      setError("Имя слишком короткое!");
    } else {
      setError("");
      alert("Отправлено!");
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={name}
        onChange={e => setName(e.target.value)}
        placeholder="Введите имя"
      />
      <button>Отправить</button>
      {error && <div style={{color: "red"}}>{error}</div>}
    </form>
  );
}

3. Как работает "use client" и смешивание компонентов

Почему нельзя просто использовать хуки в Server Components?

Server Components не попадают в клиентский бандл, они не знают о браузере и не могут хранить состояние между рендерами на клиенте. Если попытаться использовать, например, useState или useEffect в Server Component — получите ошибку: "React Server Components does not support useState" (и это не баг, а фича).

Как Next.js отличает Client и Server Components?

  • Server Component: нет директивы "use client" — исполняется только на сервере, не может использовать хуки.
  • Client Component: есть директива "use client" — исполняется на клиенте, может использовать хуки, работать с DOM, слушать события.

Аналогия: Представьте, что у вас есть два типа работников: одни сидят в офисе (сервер), другие работают "в полях" (клиент). Офисные работники могут делать отчёты, но не могут общаться с клиентами напрямую. А вот "полевые" могут реагировать на запросы клиентов, но иногда им нужны данные из офиса.

Можно ли вкладывать компоненты друг в друга?

  • Server Component может рендерить другие Server Components и Client Components.
  • Client Component может рендерить только Client Components (или обычные React-компоненты без "use client", которые не используют серверные возможности).

Важно: Если Server Component импортирует Client Component, то только дочерний компонент станет клиентским, а родитель останется серверным. Это позволяет делать гибридные страницы: большая часть рендерится на сервере, а только маленькие "островки" интерактивности становятся клиентскими.

Схема смешивания компонентов


[Server Component]
    ├── [Server Component]
    ├── [Client Component] ("use client")
    │        ├── [Client Component]
    │        └── [...]
    └── [Server Component]

4. Практика: внедряем интерактивность в приложение

В рамках вашего учебного приложения (например, список задач) мы можем добавить интерактивный компонент — например, фильтр задач или кнопку "Добавить задачу".

Пример: Фильтр задач (TaskFilter)


"use client";
import { useState } from "react";

export default function TaskFilter({ onFilter }) {
  const [query, setQuery] = useState("");

  function handleChange(e) {
    setQuery(e.target.value);
    onFilter(e.target.value);
  }

  return (
    <input
      type="text"
      placeholder="Поиск по задачам..."
      value={query}
      onChange={handleChange}
    />
  );
}

Как использовать TaskFilter в Server Component


// app/tasks/page.jsx (Server Component)
import TaskFilter from "./TaskFilter"; // Client Component

export default async function TasksPage() {
  // Загрузка задач с сервера
  const tasks = await getTasks();

  // Здесь нельзя использовать useState напрямую,
  // но можно передать callback в клиентский компонент
  return (
    <div>
      <h1>Список задач</h1>
      <TaskFilter onFilter={query => { /* фильтрация задач */ }} />
      {/* Список задач */}
    </div>
  );
}

Обратите внимание: Server Component может использовать Client Component, но не наоборот! Если вы попытаетесь импортировать Server Component внутрь Client Component — получите ошибку.

5. Полезные нюансы

Особенности Client Components

Преимущества:
  • Интерактивность: хуки, обработка событий, доступ к браузерным API.
  • Изоляция интерактивности: можно делать только нужные части страницы клиентскими, остальное — серверным (это экономит размер бандла и ускоряет загрузку).
  • Совместимость с React-библиотеками: если библиотека требует хуки или эффекты, используйте её только внутри Client Component.
Недостатки:
  • Больше JavaScript на клиенте: всё, что находится внутри Client Component, попадает в бандл (и скачивается пользователем).
  • Нет доступа к серверным функциям: нельзя напрямую вызывать серверные API, читать из базы данных и т.п.
  • Нельзя импортировать Server Component: если нужен серверный код, делайте его "снаружи" и передавайте данные через пропсы.

Где использовать "use client"

  • Только в самом верху файла компонента (до любых импортов, комментариев и т.д.).
  • Только в файлах, которые действительно должны быть интерактивными.
  • Не нужно писать "use client" в каждом файле — используйте только для "островков" интерактивности.

Как понять, что компонент должен быть клиентским?

Задайте себе вопросы:

  • Использую ли я хуки (useState, useEffect и т.п.)?
  • Работаю ли я с DOM напрямую?
  • Слушаю ли я события пользователя?
  • Использую ли я стороннюю React-библиотеку, которая требует хуки?

Если хотя бы на один вопрос ответ "да" — компонент должен быть клиентским.

Можно ли смешивать Client и Server Components?

Да! Это и есть главная фишка Next.js 15. Вы можете строить страницы из "больших кусков" серверного рендера, а интерактивные элементы делать маленькими клиентскими компонентами. Такой подход называется islands architecture («архитектура островков»).

6. Типичные ошибки и нюансы

Ошибка №1: забыли написать "use client" — не работает интерактивность.

Если вы используете хуки, но забыли директиву, получите ошибку: "React Server Components does not support useState". Просто добавьте "use client" в начало файла.

Ошибка №2: написали "use client" везде — страница стала "толстой" и медленной.

Не стоит делать всё приложение клиентским! Это убивает преимущества Server Components. Делайте клиентскими только то, что реально должно реагировать на пользователя.

Ошибка №3: пытаетесь импортировать Server Component в Client Component.

Это не сработает: клиентский компонент не может напрямую использовать серверные. Если нужно получить серверные данные — делайте загрузку данных выше по иерархии (в серверном компоненте) и передавайте их через пропсы.

Ошибка №4: используете серверные API (например, чтение из базы данных) внутри Client Component.

Такое не сработает: в клиентском компоненте нет доступа к серверу напрямую. Для этого используйте Server Actions, API-эндпоинты или передавайте данные через пропсы.

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ