Адыяваньне кампанетаў падаючага: адрасаванне контэксту з компанентаў сервера
Дазвольце даклэ расказаць, як React 19.3 дазволяе компаненту сервера безпасылкова адрадзіваць кантэкст кліента, як Flight яго серыялізуе, і якія обмежэння ў чытанні та значэннях яшчэ дзейнаюць.
Крыніцы: 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)выклекчвае бяг.
<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', таму кэпсулы-прадастальнікі стаюць непатрэбнымі.