Галоўная / Артыкулы

Адыяваньне кампанетаў падаючага: адрасаванне контэксту з компанентаў сервера

Дазвольце даклэ расказаць, як React 19.3 дазволяе компаненту сервера безпасылкова адрадзіваць кантэкст кліента, як Flight яго серыялізуе, і якія обмежэння ў чытанні та значэннях яшчэ дзейнаюць.

Выдавец: REACTAPP.TOP7 хв чытання1602 слоў

Крыніцы: React 19.3: Render a Context Directly in a Server Component, No Provider Wrapper

Практычна кожны кодбаза React Server Components містіць невялікі файл 'use client', якім ўсё, што трэба, — стварыць Context і выеўнуць компонент-прадастальнік, який перадае адзін проп. Штось такое рэшэння было неабходнае, таму што серверным компонентам не дазволялася сама ствараць Context. У версіі React 19.3 гэтыя обмежэння былі зняты, таму сервер тепер можа безпосередньа выкарыстоўваць элемент Context, і такі рэшэння ўжо не патрэбны.

У гэтым кялічыку практычна адпрацоўка старага шаблона, новага шаблона, таго, каму на самай справе прызначаны протакол Flight, а таксама аспектаў, якія важлівы для таго, каб змены працавалі ў вашай платформе сёння. Для прыкладаў трэбуея рэальны час адработкі Server Components (платформа, якая падтрымлеўчык RSC bundler і сервер), таму спрацаввайце іх як готовы код-джэрану, а не як тое, што трэба запісаць у сандбокс. Чыбаў, як раз’едначуюцца серверная і кліентская часткі RSC-проекта, паглядзіце на нашы аптак урок архітектуру, якая стоіць за React Server Components.

Стары шаблон: компанент, які толькі перадае проп

Компаненты сервера не можу вызваць createContext, таму што Context — это механізм стану на боку кліента. Аднак дрэво кліента пад іх наглядам регулярна патрабуе даны, якія вже ў сервера, такія як залогаваны корыстнік, флагі функцый кліента чыясьцо запиту. Ядзейны спосаб пераказа даных між бокамі быў через прыемнікі (props) компаненту кліента, і гэты компанент кліента пасля таго атрыбутаваў паданнік (provider).

Модуль кліента пачынаецца з стандартных дырэктываў і імпортаў.

'use client';
import { createContext } from 'react';

Пасля таго ён стварае Context і экспортуе паданнік, якога карысця заключаецца толькі у перадачы currentUser як value.

export const UserContext = createContext(null);// exists only to forward one prop
export function UserProvider({ currentUser, children }) {
  return (
    <UserContext value={currentUser}>
      {children}
    </UserContext>
  );
}

Бок компаненту сервера

Лейаут, які ёсць компанентам сервера, імпортуе обгоркі (wrapper), а не Context.

import { UserProvider } from './user-context';

Ён запрашвае даны корыстніка на сервера і перадае іх обгоркі як прыемнік.

export async function Layout({ children }) {
  const currentUser = await getCurrentUser();
  return (
    <UserProvider currentUser={currentUser}>
      {children}
    </UserProvider>
  );
}

Якща вы прабавіліся прыбыць за межы «обгорака» у React 19.2, вы страждалі ад плутанага бяговага каштоўкі, які пачынаеся з фразы «Element type is invalid», а потым скаржыцца на тое, што тип ледзь запускаемага элемента быў ператворены ў Context, а не ў клас чы функцію. Це выглядае як проблема з пакетавальнікам, але прычына крыўцяе ў рэндерары. Context, імпортаваны за межамі кліента, доходзіць да сервернага рэндерара як кліентскі кансэрт, які ператвараецца ледзь запускаема, і 19.2 могў ператварыць тыя кансэрты толькі ў компоненты. Об’ект Context ніколі не быў прыйнятым рэзультатам.

Новы падход: рэндаруецца сам Context

У версіі 19.3 кліентскі модуль складаецца з дырэктывы, імпорту і адного экспорту.

'use client';
import { createContext } from 'react';

Компонента больш не існуе, застаўся толькі об’ект Context.

export const UserContext = createContext(null);

Яго рэндараванне з лейауту

Лейаут тепер імпортуе UserContext безпосередна.

import { UserContext } from './user-context';

JSX мае той самы лепт, як і ранейш, за выключэнням таго, што элемент — это Context, а проп называецца value.

export async function Layout({ children }) {
  const currentUser = await getCurrentUser();
  return (
    <UserContext value={currentUser}>
      {children}
    </UserContext>
  );
}

Іспытанне на кліенте

Для користувачаў нічога не змянілася. Кліентскі компонент, які знаходзится нижэй у структуре, імпортуе той самы Context і чытае яго за дапамою use.

'use client';
import { use } from 'react';
import { UserContext } from './user-context';

Тут меню з аватарам выкарыстоўвае данні з Context і атрыбутуе імі зображэнне.

export function AvatarMenu() {
  const user = use(UserContext);
  return <img src={user.avatar} alt={user.name} />;
}

Зверніце увагу на сынтаксіс React 19 у структуре: там выкарыстоўваецца <UserContext value> у замену на <UserContext.Provider value>. Форма з .Provider продовжвае працаваць на кліенте, але сервер выкарыстоўвае сам об’ект Context, таму ў кодзе для сервера лепей застаўці болей короткую форму.

Што робіць Flight, і чаго не робіць гэтая функцыя

Калі компанента сервера адрадзіць <UserContext value={currentUser}>, на серверы ўжо не выканаецца жадная логіка Контэкста. Серыявальнік Flight выдае элемент, які ўзначаеся як кантэкстная рэферэнція, якая вядуе да модуля user-context.js і яго экспорту UserContext. Элемент несе { value: currentUser, children } як сваія пропы. Шырокія гэтых пропоў серыявуюцца так сама, як і будзь-якія іншыя пропы, якія пераходзяць да кліента. Калі браузер заваносі гэты модуль, кантэкстная рэферэнція ператвараецца на справжні об’ект Контэкста, і React стварае прадастальніка, які зберагае пераданую значэнне. Рашэння прыбліжнага адраджэння, задокументаванае ў facebook/react #35675, научыла шлях коду з ледзьве выкананым типам прыймаць об’ект Контэкста падобна да функцый, memo і forwardRef.

З гэтага дзяйства вынікаюць два наследкі.

Значэнне павінна быць серыявальным

Значэнне Context павінна адпавядаць тым жа правілам, як і будзь-яе атрыбут, яке прабывае да кліента. Дазволеныя типы включаюць прымітывы, такія як строкі і цалыкі, звычныя об’екты і масівы, экземпляры Date, Map і Set, promises, элементы JSX і функціі Server. Звычныя функцыі, экземпляры класоў і ўсё, што мае спецыяльны протатып, не падходзяць. Звычны запис currentUser працюе; экземпляр UserService з методамі — няўжо. У дасведчэнні 'use client' прадстаўленае ўсё полное спісак.

Это не Context з боку сервера

У прыемнай заявцы чыста пазначанае, што яшчэ не можна вызваць use(Context) унутры Server Component. Server Components не маюць можлівасці чытаць Context, таму што на сервере не існуе прадастальніка, які б ўможлівіў іх чытанне. Гэтая функцыя ёсць проста корачайшым спосабам перадачы значэння да дрэва кліента.

Чаму варта адмахнуцца ад обгорткі

Кожная спільна значэнне раней выклікала патрэбу ў аднам файле і адной складовай. Гэта здаецца толькі формальнасцю, пакуль вы іх не пачнете лічыць: прыкладом можа служыць дапрыяж, якое мае контэксты для пользователя, тэму, регіону, флагі функцый, інформацыю пра аб’екты-аренднікі та даны для аналізу — ён містіць шасць практычна ідэнтычных обёртакоў, і кожны з іх ёсць прычыной можлівасці забыць які-небудзь параметр аб памылкальна задаць значэнне за змовчанням. За дапамогою новай формы прадаўцу стае можлівасць працаваць безпосередна там, дзе запоўнююцца даны, і такім чынам зникае адзин спецыяльны каскад у ментальной модэлі „калікі часткі працуюць на сервере, а калікі — на кліянтэ“.

Реакцыя спальні на гэтыя змены была пераважна полегчэнням, а не адзіваннем. Разработчыкі зазначалі, што дзівна, што такое спачатку не падтрымвалася, адколі значэнне контэксту на самай сутнасці ёсць проста даныя, якія зберагаюцца ў памяці. Гэта інтуўіцыя правильная, і ёна поясняе, чаму сама палэкцыя ў React была незначной.

Аспекты, на якія трэба зважыць перш чым мігруваць

  • Якірны ўстаткуванне павінна выдаваць пакеты 19.3 Flight. Змяны знаходзяцца ў react-server-dom-webpack, -turbopack і -parcel а таксама ў компоненте reconciler, а не толькі ў react. Пераканайцеся, якая версія React задаўтае ваша юстаркуванне; несувязаная версія можа спрычыніць той самы бяг у формате „Element type is invalid“, пра які йшлося раней.
  • Контэкст трэба задаць у файле, пазначаным 'use client'. Контэкст, заданный у коде сервера, не можа быць атрыбутам для рэндарування, таму што весь механізм залежыць ад наявнасці гэтага об’екта ў пакете кліента.
  • Чытанне дадзеных на сервере заўсёды немагчыма. Компонент сервера, які патрэбуе гэтыя дадзеныя, должен яны отрымаць як аргумент або сам выкарыстаць функцыю для ўзяцца іх; у такім случае use(Context) выклекчвае бяг.
  • Дзеяць толькі серыялізавальныя значэнні. Якщо старое значэнне было экзаментам класа або мела прыўязаныя методы, кэпсулавальны элемент яго хаваў, і проблема тады выражалася як адказ серыялізацыі.
  • Promise ёсць дзеячае значэнне. Паколькі promises серыялізуюцца па межах, запіс <UserContext value={getCurrentUser()}> без await працюе з значэннем у режыме стріму, і корыстувальнікі должны яго два разы распакаваць за дапамою use(use(UserContext)). Гэта можа быць корыстным методам стрімування, але таксама лёгка падмаціцца, забывшы дадзець await.
  • Стандартныя значэнні застаюцца прыбліжэнням кліента. Стандартнае значэнне, якое пасылаецца ў createContext(null), прыменяецца там, дзе над корыстувальнікам няма прадаўцы, і выкарыстоўванне прадаўцы на сервере гэта не змінюе.
  • Наразы застацца з кэпсулавальным элементам

    Якщо ваша платформа яшчэ не перейшла на версію 19.3, залишыце кэпсулу. Шаблон UserProvider працюе ў кожнай версіі React 19 і ў будзь-якай платформе. Калі вы апгрэйдаваце, міграцыя ўжо простая:

    • Удаліце функцыю-кэпсулу з кліентскага модуля.
    • Імпортуйце Context у серверскі компонент заместо кэпсулы.
    • Перазначыце пераданыя параметры на value.

    Вы можете мігруваць файл за файлам, калі ваша платформа пераходзіце на 19.3. Няма прычыны пераканваты все заодно, і калі оба варыянты будуць працаваць, межваецца ніякой разлік у часе выканання.

    Галоўныя выводы

    • У React 19.3 серверскі компонент можа атрыбутаваць Context, экспортаваны з модуля 'use client', таму кэпсулы-прадастальнікі стаюць непатрэбнымі.
  • У сэрварскай частцы Flight адправляецца кансэльтатывальны ідэнтыфікатор кліента плюс серыяваныя параметры; на сэрвары не запускаецца жадна дзейнасць Context.
  • Значэнне павінна адпавядаць стандартным правілам серыявання, і сэрварскія компоненты такім чынам усё раву не можаць чытаць Context.
  • Пераканайцеся, што ваша фрэймворк сумавляе неабяжлівыя пакеты Flight, прытым усунуўшы абгорткі, і стараецеся не падпускнуць аднойчыны значэнняў у формате promise.
  • Для дапамогі прачытайце зьместкі выпуску React 19.3 на react.dev асабліва рэферэнсы па сэрварскімі компонентам.