'use client'

'use client' потрібен лише якщо ви використовуєте компоненти сервера React або створюєте сумісну з ними бібліотеку.

'use client' дозволяє позначити, який код виконується на клієнтській стороні.


Довідник

'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', імпортується з серверного компонента, сумісні пакувальники розглядатимуть імпорт модуля як межу між кодом, що виконується на сервері, та кодом, що виконується клієнтом.

Як залежності RichTextEditor, formatDate і Button також будуть обчислені на клієнтській стороні незалежно від того, чи містять їх модулі директиву 'use client'. Зверніть увагу, що один і той самий модуль може бути обчислено на сервері при імпорті з серверного коду і на клієнтській стороні при імпорті з клієнтського коду.

Застереження

  • 'use client' мають бути на самому початку файлу, над будь-яким імпортом або іншим кодом (коментарі допускаються). Вони повинні бути написані в одинарних або подвійних лапках, але не в зворотних.
  • Коли модуль 'use client' імпортується з іншого клієнтського модуля, директива не має ефекту.
  • Коли модуль компонента містить директиву 'use client', будь-яке використання цього компонента гарантовано буде клієнтським. Втім, компонент все одно можна обчислити на клієнтській стороні, навіть якщо він не має директиви 'use client'.
    • Використання компонента вважається клієнтським, якщо його визначено у модулі з директивою 'use client' або якщо він є транзитивною залежністю модуля, який містить директиву 'use client'. В іншому випадку це серверний компонент.
  • Код, який позначено для оцінки клієнтом, не обмежується компонентами. Весь код, який є частиною піддерева модуля Client, надсилається клієнту і виконується ним.
  • Коли модуль, що обчислюється сервером, імпортує значення з модуля 'use client', значення повинні бути або компонентом React, або підтримуваними серіалізованими значеннями пропсів для передачі клієнтському компоненту. Будь-який інший варіант використання згенерує виключення.

Як 'use client' позначає код клієнта

У React-застосунку компоненти часто розділяють на окремі файли, або модулі.

Для застосунків, які використовують Компоненти сервера Reacts, застосунок рендериться на сервері за замовчуванням. 'use client' вводить межу сервер-клієнт у дереві залежностей модуля , фактично створюючи піддерево клієнтських модулів.

Щоб краще проілюструвати це, розглянемо наступний застосунок Компонентів сервера React.

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>
    </>
  );
}
export default function FancyText({title, text}) {
  return title
    ? <h1 className='fancy title'>{text}</h1>
    : <h3 className='fancy cursive'>{text}</h3>
}
'use client';

import { useState } from 'react';
import inspirations from './inspirations';
import FancyText from './FancyText';

export default function InspirationGenerator({children}) {
  const [index, setIndex] = useState(0);
  const quote = inspirations[index];
  const next = () => setIndex((index + 1) % inspirations.length);

  return (
    <>
      <p>Your inspirational quote is:</p>
      <FancyText text={quote} />
      <button onClick={next}>Inspire me again</button>
      {children}
    </>
  );
}
export default function Copyright({year}) {
  return <p className='small'>©️ {year}</p>;
}
export default [
  "Don’t let yesterday take up too much of today.” — Will Rogers",
  "Ambition is putting a ladder against the sky.",
  "A joy that's shared is a joy made double.",
];
.fancy {
  font-family: 'Georgia';
}
.title {
  color: #007AA3;
  text-decoration: underline;
}
.cursive {
  font-style: italic;
}
.small {
  font-size: 10px;
}

У дереві залежностей модулів цього прикладу програми директива '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, позначаючи 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. InspirationGenerator та його дочірній компонент FancyText є компонентами, експортованими з клієнтського коду і вважаються клієнтськими компонентами.

Вводимо наступні визначення:

  • Клієнтські компоненти - це компоненти у дереві зображування, які зображуються на клієнтській стороні.
  • Серверні компоненти - це компоненти у дереві рендерингу, які рендериться на сервері.

У прикладі програми App, FancyText та Copyright є серверними компонентами і вважаються серверними компонентами. Оскільки InspirationGenerator.js та його транзитивні залежності позначено як клієнтський код, компонент InspirationGenerator та його дочірній компонент FancyText є клієнтськими компонентами.

Яким чином FancyText є одночасно і серверним, і клієнтським компонентом?

Згідно з наведеними вище визначеннями, компонент FancyText є одночасно і серверним, і клієнтським компонентом, як таке може бути?

По-перше, давайте уточнимо, що термін "компонент" не дуже точний. Ось лише два способи розуміння терміну "компонент":

  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 />;
}

Часто неточність не є важливою при поясненні понять, але у цьому випадку вона є.

Коли ми говоримо про серверні або клієнтські компоненти, ми маємо на увазі використання компонентів.

  • Якщо компонент визначено у модулі з директивою 'use client', або компонент імпортовано та викликано у клієнтському компоненті, то використовується клієнтський компонент.
  • В іншому випадку компонент використовується як серверний компонент.
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 позначає його як серверний компонент. Коли FancyText імпортується і викликається з InspirationGenerator, таке використання FancyText є клієнтським компонентом, оскільки InspirationGenerator містить директиву 'use client'.

Це означає, що визначення компонента для FancyText буде обчислено на сервері, а також завантажено клієнтом для відтворення його клієнтського компонента

.

Оскільки Copyright зображено як нащадка клієнтського компонента InspirationGenerator, ви можете здивуватися, що це серверний компонент.

Нагадаємо, що '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 відображається на сервері, оскільки він є кореневим компонентом.

Клієнтські компоненти можуть рендерити серверні компоненти, оскільки ви можете передавати JSX як пропси. У цьому випадку InspirationGenerator отримує Copyright як дочірні. Однак модуль InspirationGenerator ніколи безпосередньо не імпортує модуль Copyright і не викликає компонент, все це робить App. Фактично, компонент Copyright повністю виконується до того, як InspirationGenerator почне рендеринг.

Висновок полягає у тому, що зв'язок рендерингу батьків і дітей між компонентами не гарантує однакового середовища рендерингу.

Коли використовувати 'use client'

За допомогою 'use client' ви можете визначити, коли компоненти є клієнтськими. Оскільки за замовчуванням використовуються серверні компоненти, ось короткий огляд переваг та обмежень серверних компонентів, щоб визначити, коли вам потрібно позначити щось як клієнтський рендер.

Для простоти, ми говоримо про серверні компоненти, але ті самі принципи застосовуються до всього коду у вашому застосунку, який виконується на сервері.

Переваги серверних компонентів

  • Серверні компоненти можуть зменшити кількість коду, що надсилається та виконується клієнтом. Тільки клієнтські модулі збираються та оцінюються клієнтом.
  • Серверні компоненти отримують переваги від запуску на сервері. Вони мають доступ до локальної файлової системи і можуть відчувати низьку затримку при отриманні даних та мережевих запитах.

Обмеження серверних компонентів

  • Серверні компоненти не можуть підтримувати взаємодію, оскільки обробники подій мають бути зареєстровані та запущені клієнтом.
    • Наприклад, обробники подій на зразок onClick можуть бути визначені лише у клієнтських компонентах.
  • Серверні компоненти не можуть використовувати більшість хуків.
    • Під час рендерингу серверних компонентів їхнє виведення - це, по суті, список компонентів для рендерингу клієнтом. Серверні компоненти не зберігаються у пам'яті після рендерингу і не можуть мати власного стану.

Серіалізовані типи, що повертаються серверними компонентами

Як і в будь-якому React-застосунку, батьківські компоненти передають дані дочірнім. Оскільки вони рендерингуються в різних середовищах, передача даних від серверного компонента до клієнтського вимагає додаткового розгляду.

Значення параметрів, що передаються з серверного компонента до клієнтського, мають бути серіалізованими.

Серіалізовані пропси включають:

  • Примітиви
  • Змінні, що містять серіалізовані значення
  • Дата
  • Прості об'єкти: створені за допомогою ініціалізаторів об'єктів, з серіалізованими властивостями
  • Функції, які є Діями сервера
  • Елементи клієнтського або серверного компонента (JSX)
  • Обіцянки
  • Зокрема, не підтримуються:

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

    Usage

    Побудова з інтерактивністю та станом

    '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 браузера для веб-сховища, маніпуляцій з аудіо та відео, апаратного забезпечення пристроїв, серед іншого.

    У цьому прикладі компонент використовує DOM API для маніпулювання елементом 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. Сторонні компоненти, які використовують будь-який з наступних React API, повинні запускатися на стороні клієнта:

    Якщо ці бібліотеки були оновлені для сумісності з серверними компонентами React, то вони вже включатимуть власні маркери 'use client', що дозволить вам використовувати їх безпосередньо з ваших серверних компонентів. Якщо бібліотека не була оновлена, або якщо компонент потребує пропсів, таких як обробники подій, які можна вказати лише на клієнтській стороні, вам може знадобитися додати власний файл клієнтського компонента між стороннім клієнтським компонентом і вашим серверним компонентом, де ви хочете його використовувати.