Spec-Zone.ru › React 18

'use client' - Эта функция доступна в последней версии Canary

Canary

'use client' требуется только если вы используете React Server Components или создаёте библиотеку, совместимую с ними.

'use client' позволяет помечать код, который выполняется на клиенте.

  • Справочник
    • 'use client'
    • Как 'use client' помечает код для клиента
    • Когда использовать 'use client'
    • Сериализуемые типы, возвращаемые Server Components
  • Использование
    • Разработка с интерактивностью и состоянием
    • Использование клиентских API
    • Использование библиотек сторонних разработчиков

Справочник

'use client'

Добавьте 'use client' в начало файла, чтобы отметить модуль и его транзитивные зависимости как клиентский код.

'use client';

import { useState } from 'react';
import { formatDate } from './formatters';
import Button from './button';

export default function RichTextEditor({ timestamp, text }) {
  const date = formatDate(timestamp);
  // ...
  const editButton = <Button />;
  // ...
}

Когда файл, помеченный 'use client', импортируется из Server Component, совместимые сборщики будут считать импорт модуля границей между кодом, выполняемым на сервере, и кодом, выполняемым на клиенте.

Зависимости RichTextEditor, formatDate и Button также будут оцениваться на клиенте независимо от того, содержат ли их модули директиву 'use client'. Обратите внимание, что один модуль может быть оценён на сервере при импорте из серверного кода и на клиенте при импорте из клиентского кода.

Ограничения

  • 'use client' должно находиться в самом начале файла, выше любых импортов или другого кода (комментарии допускаются). Они должны быть написаны в одинарных или двойных кавычках, но не в обратных кавычках.
  • Когда модуль, помеченный 'use client', импортируется из другого модуля, отображаемого на клиенте, директива не имеет эффекта.
  • Когда модуль компонента содержит директиву 'use client', любое использование этого компонента гарантированно будет Client Component. Однако компонент всё ещё может быть оценен на клиенте, даже если он не содержит директивы 'use client'.
    • Использование компонента считается Client Component, если оно определено в модуле с директивой 'use client' или если это транзитивная зависимость модуля, содержащего директиву 'use client'. В противном случае это Server Component.
  • Код, отмеченный для оценки на клиенте, не ограничивается компонентами. Весь код, являющийся частью поддерева модуля Client, отправляется на клиент и выполняется им.
  • Когда сервер оценивает модуль, импортирующий значения из модуля 'use client', эти значения должны быть либо React-компонентом, либо поддерживаемыми сериализуемыми значениями свойств, чтобы передать их Client Component. Любой другой случай использования приведёт к исключению.

Как 'use client' помечает код для клиента

В приложении React компоненты часто разбиваются на отдельные файлы или модули.

Для приложений, использующих React Server Components, приложение по умолчанию рендерится на сервере. 'use client' вводит серверно-клиентскую границу в дереве зависимостей модулей, фактически создавая поддерево Client-модулей.

Для лучшей иллюстрации рассмотрим приложение React Server Components.

import FancyText from './FancyText';
import InspirationGenerator from './InspirationGenerator';
import Copyright from './Copyright';

export default function App() {
  return (
    <>
      <FancyText title text="Get Inspired App" />
      <InspirationGenerator>
        <Copyright year={2004} />
      </InspirationGenerator>
    </>
  );
}

В дереве зависимостей модулей этого примера директива 'use client' в InspirationGenerator.js помечает этот модуль и все его транзитивные зависимости как клиентские модули. Поддерево, начинающееся с InspirationGenerator.js, теперь помечено как клиентские модули.

A tree graph with the top node representing the module 'App.js'. 'App.js' has three children: 'Copyright.js', 'FancyText.js', and 'InspirationGenerator.js'. 'InspirationGenerator.js' has two children: 'FancyText.js' and 'inspirations.js'. The nodes under and including 'InspirationGenerator.js' have a yellow background color to signify that this sub-graph is client-rendered due to the 'use client' directive in 'InspirationGenerator.js'.

'use client' сегментирует дерево зависимостей модулей приложения React Server Components, помечая InspirationGenerator.js и все его зависимости как рендерящиеся на клиенте.

Во время рендеринга фреймворк рендерит корневой компонент на сервере и продолжает работу по дереву рендеринга, отказываясь от оценки любого кода, импортированного из клиентского кода.

Затем серверная часть дерева рендеринга отправляется на клиент. Клиент, с загруженным клиентским кодом, завершает рендеринг остальной части дерева.

A tree graph where each node represents a component and its children as child components. The top-level node is labelled 'App' and it has two child components 'InspirationGenerator' and 'FancyText'. 'InspirationGenerator' has two child components, 'FancyText' and 'Copyright'. Both 'InspirationGenerator' and its child component 'FancyText' are marked to be client-rendered.

Дерево рендеринга для приложения React Server Components. InspirationGenerator и его дочерний компонент FancyText являются компонентами, экспортируемыми из кода, отмеченного как клиентский, и считаются Client Components.

Мы вводим следующие определения:

  • Client Components — компоненты в дереве рендеринга, рендерящиеся на клиенте.
  • Server Components — компоненты в дереве рендеринга, рендерящиеся на сервере.

Работая с примером приложения, App, FancyText и Copyright все рендерятся на сервере и считаются Server Components. Так как InspirationGenerator.js и его транзитивные зависимости помечены как клиентский код, компонент InspirationGenerator и его дочерний компонент FancyText являются Client Components.

Подробное рассмотрение

Как FancyText является и Server, и Client Component?

Согласно вышеприведённым определениям, компонент FancyText является как Server, так и Client Component, как это возможно?

Сначала давайте уточним, что термин «компонент» не очень точный. Вот два способа, как «компонент» можно понять:

  1. «Компонент» может относиться к определению компонента. В большинстве случаев это будет функция.
// This is a definition of a component
function MyComponent() {
  return <p>My Component</p>
}
  1. «Компонент» также может относиться к использованию компонента его определения.
import MyComponent from './MyComponent';

function App() {
  // This is a usage of a component
  return <MyComponent />;
}

Часто неточность не имеет значения при объяснении концепций, но в этом случае это важно.

Когда мы говорим о Server или Client Components, мы имеем в виду использование компонента.

  • Если компонент определен в модуле с директивой 'use client', или компонент импортирован и вызван в Client Component, то использование компонента является Client Component.
  • В противном случае использование компонента является Server Component.
A tree graph where each node represents a component and its children as child components. The top-level node is labelled 'App' and it has two child components 'InspirationGenerator' and 'FancyText'. 'InspirationGenerator' has two child components, 'FancyText' and 'Copyright'. Both 'InspirationGenerator' and its child component 'FancyText' are marked to be client-rendered.
Дерево рендеринга иллюстрирует использование компонентов.

Вернёмся к вопросу о FancyText, мы видим, что определение компонента не имеет директивы 'use client' и у него есть два использования.

Использование FancyText в качестве дочернего элемента App помечает это использование как Server Component. Когда FancyText импортируется и вызывается под InspirationGenerator, это использование FancyText является Client Component, так как InspirationGenerator содержит директиву 'use client'.

Это означает, что определение компонента для FancyText будет оцениваться как на сервере, так и загружаться на клиент для рендеринга его Client Component-использования.

Подробное рассмотрение

Почему Copyright является Server Component?

Поскольку Copyright рендерится в качестве дочернего элемента Client Component InspirationGenerator, вы можете удивиться, что это Server Component.

Вспомните, что 'use client' определяет границу между серверным и клиентским кодом в дереве зависимостей модулей, а не в дереве рендеринга.

A tree graph with the top node representing the module 'App.js'. 'App.js' has three children: 'Copyright.js', 'FancyText.js', and 'InspirationGenerator.js'. 'InspirationGenerator.js' has two children: 'FancyText.js' and 'inspirations.js'. The nodes under and including 'InspirationGenerator.js' have a yellow background color to signify that this sub-graph is client-rendered due to the 'use client' directive in 'InspirationGenerator.js'.

'use client' определяет границу между серверным и клиентским кодом в дереве зависимостей модулей.

В дереве зависимостей модулей мы видим, что App.js импортирует и вызывает Copyright из модуля Copyright.js. Так как Copyright.js не содержит директивы 'use client', использование компонента рендерится на сервере. App рендерится на сервере, так как это корневой компонент.

Client Components могут рендерить Server Components, потому что вы можете передавать JSX в качестве свойств. В этом случае, InspirationGenerator получает Copyright в качестве дочерних элементов. Однако, модуль InspirationGenerator никогда напрямую не импортирует модуль Copyright и не вызывает компонент, все это делает App. Фактически, компонент Copyright полностью выполняется до того, как начинается рендеринг InspirationGenerator.

Вывод заключается в том, что родительско-дочерние отношения между компонентами не гарантируют одинаковую среду рендеринга.

Когда использовать 'use client'

С помощью 'use client' вы можете определить, когда компоненты являются Client Components. Поскольку Server Components являются по умолчанию, вот краткий обзор преимуществ и ограничений Server Components, чтобы определить, когда вам нужно отметить что-то как рендерящееся на клиенте.

Для простоты мы говорим о Server Components, но те же принципы применяются ко всему коду вашего приложения, который выполняется на сервере.

Преимущества Server Components

  • Server Components могут уменьшить количество кода, отправляемого и выполняемого клиентом. Только клиентские модули собираются и оцениваются клиентом.
  • Server Components выигрывают от выполнения на сервере. Они могут получить доступ к локальной файловой системе и могут испытывать низкую задержку при запросах данных и сетевых запросах.

Ограничения Server Components

  • Server Components не могут поддерживать взаимодействие, так как обработчики событий должны быть зарегистрированы и вызваны клиентом.
    • Например, обработчики событий, такие как onClick могут быть определены только в Client Components.
  • Server Components не могут использовать большинство хуков.
    • Когда Server Components рендерятся, их вывод по существу представляет собой список компонентов, которые клиент должен рендерить. Server Components не сохраняются в памяти после рендеринга и не могут иметь собственного состояния.

Сериализуемые типы, возвращаемые Server Components

Как и в любом приложении React, родительские компоненты передают данные дочерним компонентам. Поскольку они рендерятся в разных средах, передача данных от Server Component к Client Component требует дополнительного рассмотрения.

Значения свойств, передаваемые из компонента сервера в компонент клиента, должны быть сериализуемыми.

Сериализуемые свойства включают:

  • Примитивы
    • строку
    • число
    • bigint
    • булево
    • undefined
    • null
    • символ, только символы, зарегистрированные в глобальной регистрации символов через Symbol.for
  • Итерируемые объекты, содержащие сериализуемые значения
    • String
    • Массив
    • Карта
    • Множество
    • TypedArray и ArrayBuffer
  • Дата
  • Простые объекты: созданные с помощью инициализаторов объектов, со свойствами, которые можно сериализовать
  • Функции, которые являются действиями сервера
  • Элементы компонента клиента или сервера (JSX)
  • Обещания

Следует отметить, что следующие не поддерживаются:

  • Функции, которые не экспортируются из модулей, помеченных как клиентские, или не помечены как 'use server'
  • Классы
  • Объекты, которые являются экземплярами любого класса (кроме встроенных), или объекты с нулевым прототипом
  • Символы, не зарегистрированные глобально, например Symbol('my new symbol')

Использование

Создание с взаимодействием и состоянием

'use client';

import { useState } from 'react';

export default function Counter({initialValue = 0}) {
  const [countValue, setCountValue] = useState(initialValue);
  const increment = () => setCountValue(countValue + 1);
  const decrement = () => setCountValue(countValue - 1);
  return (
    <>
      <h2>Count Value: {countValue}</h2>
      <button onClick={increment}>+1</button>
      <button onClick={decrement}>-1</button>
    </>
  );
}

Поскольку Counter требует как хука useState, так и обработчиков событий для увеличения или уменьшения значения, этот компонент должен быть клиенским компонентом и потребует директиву 'use client' вверху.

В отличие от этого, компонент, отображающий пользовательский интерфейс без взаимодействия, не должен быть компонентом клиента.

import { readFile } from 'node:fs/promises';
import Counter from './Counter';

export default async function CounterContainer() {
  const initialValue = await readFile('/path/to/counter_value');
  return <Counter initialValue={initialValue} />
}

Например, родительский компонент Counter, CounterContainer, не требует 'use client', так как он не интерактивен и не использует состояние. Кроме того, CounterContainer должен быть компонентом сервера, так как он считывает данные из локальной файловой системы на сервере, что возможно только в компоненте сервера.

Также существуют компоненты, которые не используют какие-либо функции, специфичные для сервера или клиента, и могут быть равнозначны для рендеринга в любом месте. В нашем предыдущем примере FancyText является таким компонентом.

export default function FancyText({title, text}) {
  return title
    ? <h1 className='fancy title'>{text}</h1>
    : <h3 className='fancy cursive'>{text}</h3>
}

В этом случае мы не добавляем директиву 'use client', в результате чего вывод FancyText (а не исходный код) отправляется в браузер при ссылке из компонента сервера. Как показано в предыдущем примере приложения Inspirations, FancyText используется как компонент сервера или клиента, в зависимости от того, где он импортирован и используется.

Но если HTML-вывод FancyText был велик по отношению к исходному коду (включая зависимости), то может быть эффективнее принудительно сделать его компонентом клиента. Одним из случаев, когда это может быть эффективнее, являются компоненты, возвращающие длинную строку SVG.

Использование API клиента

Приложение React может использовать API, специфичные для клиента, такие как API браузера для работы с веб-хранилищем, обработкой аудио и видео и оборудованием устройства, среди других.

В этом примере компонент использует API DOM для изменения элемента canvas. Поскольку эти API доступны только в браузере, он должен быть помечен как компонент клиента.

'use client';

import {useRef, useEffect} from 'react';

export default function Circle() {
  const ref = useRef(null);
  useLayoutEffect(() => {
    const canvas = ref.current;
    const context = canvas.getContext('2d');
    context.reset();
    context.beginPath();
    context.arc(100, 75, 50, 0, 2 * Math.PI);
    context.stroke();
  });
  return <canvas ref={ref} />;
}

Использование сторонних библиотек

Часто в приложении React вы используете сторонние библиотеки для обработки общих шаблонов пользовательского интерфейса или логики.

Эти библиотеки могут опираться на хуки компонентов или API клиента. Сторонние компоненты, использующие любые из следующих API React, должны работать на клиенте:

  • createContext
  • react и react-dom хуки, за исключением use и useId
  • forwardRef
  • memo
  • startTransition
  • Если они используют API клиента, например, вставку DOM или нативные виджеты платформы

Если эти библиотеки были обновлены для совместимости с React Server Components, то они уже будут содержать маркеры 'use client', позволяя использовать их напрямую из ваших компонентов сервера. Если библиотека не обновлена или компонент требует свойств, таких как обработчики событий, которые могут быть заданы только на клиенте, вам может потребоваться создать свой файл компонента клиента между сторонним компонентом клиента и компонентом сервера, где вы хотите его использовать.

© 2013–present Facebook Inc.
Licensed under the Creative Commons Attribution 4.0 International Public License.
https://18.react.dev/reference/rsc/use-client

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API