React 19.3 – за межамі загадак: аплікант, параметры Fullscreen і размах пакета
Практычны ўзлёт пра менш значыцьвенныя змены ў React 19.3: атрыбут submitter, прызнакі канферэнцыі fullscreen, паралельныя пераходы, функцыя onReset, значныя практыкі багоў і рэальнае збільшэнне размеру.
У прытаках даўнейшых версіяў часта выделяюцца калькі найважлівейшых функцыяй, і React 19.3 не ўзнікненне. Пад імі распаковваецца спіс „іншых значных змян“ плюс довгая сторанка з выправленнямі бягоў, большасць з яых — це адна лінія, якую лёгка праціснуць. Аднак калькі з гэтых ліняйкаў зменяюць спосаб напісання компонентаў сёння, апяшваюць бягі, з якімі вы можаце ўжо справіліся, або вплываюць на тое, што вы надаеце корыстнікам. Шэры розглядае іх працышна па той порядку, якім вони можуць запанаваць над вамі, і завершаецца адпаведным адказам на пытанне, якое задаюць усі пасля апдэйта: насколькі большы стаў бандл?
Ведаць, який кантактная плячка адправіла форму
Форма з двумя кнопкамі для надсылання, напрыклад Save і Save & close, павінна знаты, яку саме кнопку нажаў корыстувач. Браузеры вялікі час адпаведзялі на гэты вопыт: натыўны SubmitEvent выклікае атрыбут submitter, який паказывае кнопку, якая спрычыніла надсыланне. Аднак сінтэтычныя зместы React гэта не перадавалі. Звычныя способы рашэння былі чытанне e.nativeEvent.submitter або, менш элегантна, запісваўненне пасляпэльна нажатае кнопкі ў стацыі і надзяра на тое, што жодна надсылка з клавіатуры не працягнёцца. У React 19.3 атрыбут submitter быў даданы да сінтэтычнага зместу надсылання (#35590).
Найпростый спосаб разгледзець разлік — запустыць адну і тую ж форму ў обох версіях. Дэман стартуе з ейных імпортаў; імпорт прастора імена ўжо толькі для таго, каб компонент могаў вывести React.version.
import { useState } from 'react';
import * as React from 'react';
Компанента запобегае стандартнаму навігаванню, а пасля запісвае версію React і значэнне e.submitter у стан. Кожная кнопка мае свой value, які самэўсёды ўжоўваецца для розгалужэння коду ў рэальным прыкладзе.
export default function App() {
const [msg, setMsg] = useState('press either button');
return (
<form onSubmit={e => {
e.preventDefault();
setMsg(`${React.version}: e.submitter → ${e.submitter ? e.submitter.value : String(e.submitter)}`);
}}>
<button value="save">Save</button>{' '}
<button value="save-close">Save & close</button>
<p className="msg">{msg}</p>
</form>
);
}
Одна правіла стылюе рэчы, якія выводзяцца, у шрыфті моноспейс.
.msg { margin: 10px 0 0; font-family: ui-monospace, monospace; }
Той самы форма ў версіі 19.3
Версія 19.3 дэманстрацыі ідентычная, і гэта самэ ўсё: змінюецца толькі версія React. Імпорты застаюцца тымі ж.
import { useState } from 'react';
import * as React from 'react';
Таксама і компанента.
export default function App() {
const [msg, setMsg] = useState('press either button');
return (
<form onSubmit={e => {
e.preventDefault();
setMsg(`${React.version}: e.submitter → ${e.submitter ? e.submitter.value : String(e.submitter)}`);
}}>
<button value="save">Save</button>{' '}
<button value="save-close">Save & close</button>
<p className="msg">{msg}</p>
</form>
);
}
І таксама і файлы з стылямі.
.msg { margin: 10px 0 0; font-family: ui-monospace, monospace; }
У версіі 19.2 у прынтаваных данных паказываецца undefined, таму што сінтэтычныя запусканні не маюць такога поля. У версіі 19.3 паказываецца save або save-close, залежна ад кнопкі. Памятаце, які шлях коду паўнажыць на гэта. Якщо вы надаеце даныя через форму Actions заместо обрабоўчыка onSubmit, React вялікай часткай вядома чыгун, які надае даныя, калі ствараў FormData (#29028), таму пара name і value нажатай кнопкі вялікай часткой ўжо была ў дадзенах дзеяння. Новая атрыбута мае значэнне для обрабоўчыкаў запускання.
Практычны паўтарак: value чыгуна дапамагае толькі тады, калі вы фактычна задаеце value (і зазвычай name) для кожной кнопкі. Кнопка без іх выдае порожняю строеку, якую лёгка спыліць з нявеснай функцыяй.
Запусканні у режыме повнае екрана як проперты першага класу
API Fullscreen адрасуе змены стану fullscreenchange і fullscreenerror на адпрацоўваным элементе. React не распазнаў гэтыя імены з’яво, таму такія компаненты, як плееры відэа, былі змушаны ручна прыўязваць слухачы за дапамою addEventListener унутрь эфекта і памяцать аб зняцці іх пад час чысткі. У React 19.3 былі даданы onFullscreenChange і onFullscreenError як звычайныя пропы (#34621).
Для дэманстрацыі патрэбны ref для элемента, які пераходзіць у режым павнага екрана, і стан для логу з’яво.
import { useRef, useState } from 'react';
Заўважыце, што оба працоўнікі знаходзяцца безпасуль на элементе div. Працоўнік змены пераглядае document.fullscreenElement, каб выявіць, чы элемент якраз увайшоў у режым повнага екрана чы выйша з яго, адтакуе ўсё таму, што сама подія вызываецца у обох направленнях. Кнопка Перейсці у режым повнага екрана таксама прыўязвае элемент .catch да обяцанні, якое вяртае requestFullscreen(), адтакуе ўсё таму, што адмова фіксуецца як адхіленае обяцанне і як падзея з бядай.
export default function App() {
const box = useRef(null);
const [log, setLog] = useState([]);
const add = line => setLog(l => [...l, line]); return (
<div
ref={box}
className="stage"
onFullscreenChange={() => add(document.fullscreenElement ? 'entered fullscreen' : 'left fullscreen')}
onFullscreenError={e => add('fullscreen error: ' + e.type)}
>
<button onClick={() => box.current.requestFullscreen().catch(err => add('rejected: ' + err.message))}>
Go fullscreen
</button>{' '}
<button onClick={() => document.fullscreenElement && document.exitFullscreen()}>Exit</button>
<pre className="log">{log.join('\n') || 'no events yet'}</pre>
</div>
);
}
Шаблон стылю выкарыстоўвае псевдоклас :fullscreen, каб центруваць контэнт, калі сцена заполняе весь экран, а таксама light-dark(), каб выбраць фон, які пасуе да схемы кольцаў.
.stage { padding: 12px; border-radius: 10px; background: light-dark(#f1f5f9, #262a33); }
.stage:fullscreen { display: grid; place-content: center; }
.log { margin: 10px 0 0; font-size: 14px; }
onFullscreenError выклікаецца, калі браузер адхоўвае запит. Два частыя прычыны — вызов requestFullscreen() паza рамкамі дзеяння корыстніка і ўработка ўнутрь <iframe>, які не мае атрыбута allow="fullscreen". Якщо вы вбудовваеце плеер на сторонней сторунцы і режым повнага екрана нічога не робіць, першым крокам якраз є перакананнеся, чыга не хватка прав для iframe, а новыя атрыбуты даўаюць можлівасць проста зафіксаваць гэты факт.
Пераходы больш не чакаюць адзін на другога
У записе прынятых змян сказана, што кожны пераход тепер відрасцвячае окрема, а не разам з іншымі ў адны загальны результат, тады павольны пераход не можа заблокаваць іншыя, некалязаныя з яным. Гэтае змена (#37290) увімкнела флаг enableParallelTransitions.
Даўжыню 19.3, якщаразамене startTransition прыйшла ў той час, калі React яшчэ адрасаваў першую, штояны зліваліся. Адрасаванне, якое велася, пачыналася занова з прымененнем обох змěн, і жадна з іх не магла быць зафіксавана, пакуль не завершылася гэтае сумаванае адрасаванне. Такім чынам, швыдкі і недорогі переход могаў стаць заручнікам дорогага перходу, з якім ён не меў нічыяго спраўа. Тепер кожны переход ёсць самастоятельной ейкавай елементам. Якща вы хочаце зразумець, як React внутршняя чыніць адлічэнне гэтых ейкаў, наша стаття пра тое, як React лейны кодуюць прыорітэт змěн раскрывае асновную модель.
Спосаб напісання функцыяў Transitions не змінюецца, і правіла арранжавання з боку React застаюцца чыннымі: пазнейшая апдэйтаванне ніколі не будзе застосавана разам з старым станом. Аднак адна з прыпускаў стае некарэткай — апдэйтаванні, выданыя через два розныя вызовы startTransition, тепер можу быць застосаваныя ў розных кадрах. Якщо калькі апдэйтаванняяў павінны з’явіцца разам, пакладзіце іх у адны вызов startTransition.
Не чакайце, што гэта будзе видна ў простым прыкладзе. Праба падтварыць гэта з медленным дрэвам з 300 компонентамі і простым шырокам скорасцю, запланаваным на 50 мс пазней, паказвала, што шырокам скорасць застосавалася пасля медленнай як у версіях 19.2, так і 19.3. Це павышэнне эфектыўнасці выклікаецца на рэвэлье цэнтрызацыі і прыкладваецца у велікіх застосоўніках пад рэальным навантажэнням, а не у маленькіх дэмах.
Формы: onReset пасля дзеяння, а таксама новая формулірацыя паведамленняў пра адказкі
Калі завершаецца адзінкцыя формы, React автаматычна скасоўвае яе. Да гэтага ён выканаў гэтае скасаванне з выключаным сваім системай запуску адбуванняяў, што значыла, што адбуванне reset не было прыйнята. Адбуванне onReset, якое мяло на мету ачысці кераваны стан, ніколі не выканалася, і кераваныя элементы вводу продовжвалі паказваць свяйшыя значэнні (#35176, што вылечвае проблему #33630). У версіі 19.3 адбуванне onReset таксама запускаецца пад час автаматычнага скасавання. Якщо вы працавалі з гэтым багам, слухаючы натыўнае адбуванне reset, вы можете вернуцца да праперата і адмовіцца ад выкарыстоўвання гэтага эфекта.
Адноснае косметычнае змена: у прывітаннях па адмоўкам з useActionState тепер упомінаецца „стан адзінкцыі“ замест „стану формы“ (#35790). Працэс застаўся незменным, але пошукі ў лагах, правілы паведамленняў чы тэсты, якія адпаведаюць стараму формулаванню, трэба апдэйтаваць.
Змены пад час разработкі: Strict Mode, use() і Fast Refresh
Тры змены паўтараюцца толькі ў версіях для разработкі, але кожная з іх можа выявіць нечаканыя проблемы пасля апдэйта.
- Strict Mode пад час гідратацыі. Эфекты тепер запускаюцца два разы пад час гідратацыі у режыме Strict Mode (#35961), што падабяжае да поведзення корэнтных элементаў, якія рэндаруюцца на кліеантзе, з часоў React 18. Якщо додатак, які рэндаруецца на серверзе, здаваўся безпечным у режыме Strict Mode толькі таму, што гідратацыя праходала без другог запуску, тады гэта стане прычыной проблем. Рашэнне як завжды: зробіць эфекты ідэмпатнымі і запэўніць ўсё неабходнае для ўсунення залишковых эфектаў.
use(). Дазволяецца выклікаць use умовна. Проблемная ситуацыя выступае, калі компонент на час завішваець выкананне через use(), а пасля перазбірання працюе па шыяне, якае ўжо зовсім не выклікае use(). У такім случае межа Suspense рашуецца з неправильной прычыны. Тэкстарыбут React тады выдае паведамленне для розрабоцкага режыму, яке пояснюе, што бібліятэка, якая завісіла выкананне через use() у пакульнай перрабатцы, прахідала выклік use() пасля таго, як завершыла сваю роботу, і што гэта ўскладнены спосаб выкорыстання гака. У формулаванні паведамлення натыякаецца, што прычыной часта ёст тэкстарыбут з дадзеннямі, а не ваш сопны компонент (#37104, #37203, #37491).lazy(), зменена функцыя порэванавання memo(), а таксама змены, якія ператвараюць компонент у іншы тип (#36965, #36964, #36963, #36950). Якща гэтыя змены інодзе вымагалі павольнага перзагрузкі всіяго сторанцы, то прычынай былі саме гэтыя факторы.Меньшыя змены ў DOM
- Апдэты, якія выкалікаюцца праз
resize, тепер агрупуюцца да наступнага кадру (#35117), так сама, як і змены, выкалікаюцыся праз события прасування. Стан, заданный у абэрбатарыresize, больш не выкарыстоўваецца раней проста таму, што ў середзіне кадру падае клік. maskTypeтепер визнаецца як атрыбут SVG (#35921).- Рэсурсы модуляў тепер можаць мець падказку пра прыорітэт: перадайте
fetchPriorityфункцыямReactDOM.preloadModuleабоReactDOM.preinitModule, і React будзе выканаць яго як атрыбутfetchpriority(#36835). Гэта дазволяе плагіну для зборкі RSC пазначаць часткі кліентскіх компанентаў як з низкім прыорітэтом. Функцыіpreloadіpreinit, якія не ўтвараюць модуляў, вэсна ўжо падтрымалі гэта. credentiallessспрацоўвае як булевы атрыбут у<iframe>(#36148), а React прапануе правільную форму напісання, якщо вы запішацеcredentialLess(#36322).- React больш не перазначае
innerHTML, калі гэты атрыбут не змяніўся (#36949). Перывыканне з аднаковымdangerouslySetInnerHTMLбольш не змушвае браузер зноў парсаваць маркап, што таксама захоўвае стан DOM унутры яго. defaultValueу элементаў зtype="number"тепер працуе так сама, як і у іншых типах элементаў (#36980).- Эвэнт
toggleкопіюе атрыбутsourceдля падпаказваючыхся элементаў (#37389).
Адрасаванне на серверы і компаненты на серверы
- Памылкі, якія выдаюцца на серверы, тепер доходзяць да кліента разам з ўсімі дадзеннямі
cause(#35810), а об’ектAggregateErrorсохраняе свой масівerrors(#36156). Код для обробкі памылак і логавання бачыць тую ж структуру як на серверы, так і на кліянцы. - Компаненты на серверы тепер можаць адрасаваць элемент
<Activity>, таму што Flight яго розумее (#34697). react-dom/serverбольш не застойваецца пад Deno (#35235), а точка запуску Bun тепер уключае ўсі API Node (#34193).
Выправленні багоў, симптамы якіх вы можаце быць бачылі
Пэўны список ёсць дужа дзягучы. Це тые пункты, якія з наібольшай верасцю адпавядаюць багу, які вы вяліка часткачас шукалі.
useDeferredValueмогаць застряць на старым значэнні (#36134). Значэнне, якое адкладаецца і часам так і не апошнічваецца, было багом у React, а не памылкай у вашам коде.- Змены контэкста не завжды доходзілі да альтернатыўных рашэнняў Suspense (#36160) або да паддрэсіны, якая заляшалася у стане Suspense за межамі (#35839).
useEffectEventунутры компонентаўforwardRefіmemoчытае старыя значэнні (#34831).- Калі дрэва
<Activity>было схаванае,useSyncExternalStoreпрацягвала не бачыць змэн у храненні (#36947). Кроме таго, схаванае дрэва не перадае свой<title>( #34983 ), не прадстаўляе своія памылкі ў видным інтерфейсе ( #35074 ) і больш не паказвае, што ён атрыбутуе ў порталы ( #35091 ).
nonce (#37030).<ViewTransition>: адні ў Mobile Safari (#35337), другі — калі яны выкарыстоўваліся разам з SuspenseList (#35520). Гэта мае значэнне, якщо вы прыменялі эксперыментальныя версіі.decodeReplyFromBusboy выкарыстоўвала толькі элементы FormData (#36468); глубокія асінхронныя ланцюгі моглі перапыліць стак, а інфармацыя для дэбагавання могла экспанавацыйна зрасаць, пакуль не выклікалася памятная пашибка RangeError (#37481).Полны список патронавальных запросаў можна знайсці ў CHANGESLOG-е React.
Скількі байтав коштае апгрэйд
Недзеўна пасля выпуску ў дыскусіях на r/reactjs быў падмет пра зростанне розмеру файла react-dom-client.production.js з 523,5 kB да 610,5 kB, і была запытана прычына. Порашчаткаваўшы пакеты 19.2.0 і 19.3.0, якія публікуюцца на unpkg, маем такія цыфры:
cjs/react-dom-client.production.jsу неспакаваным формате: 536,016 B у версіі 19.2.0 проты 625,168 B у версіі 19.3.0, тое ўскладненне на 89 kB (16,6 %).- Той самы файл у формате gzip з рэжымам 9: 94,7 kB проты 110,5 kB, тое ускладненне на 15,7 kB.
cjs/react-dom-server.node.production.jsу неспакаваным формате: 277,046 B проты 300,544 B, тое ускладненне на 23,5 kB.cjs/react.production.jsу неспакаваным формате: 17,217 B проты 18,040 B, тое ускладненне на 0,8 kB.
Цыфры ў каментарах на Reddit адпраўленыя ў тых жа размерах, выражаных у KiB. Прычына простая: элементы View Transitions, Fragment Refs, browser() і кодавы шлях Trusted Types былі ранейш супакоўваны за дапамогай фічарных флагаў, якія выключылі іх у стабільнай версіі. У версіі 19.3 яны зноў ўключаныя. Праз сеть разліка становіць праўда 16 kB пасля супакоўкі, што парадна з наборам ікон середньага розміру.
Это справжня витрата, якую варта вылічыць у бюджэте на аптимізацыю працяздатнасі, але гэта не ўскладненне у працаванні React. Якщо додатковыя байты маюць значэнне для вашага дапрыемства, найэфектывыя спосабы — это раздзеленне коду, скорачэнне власных залежнасцей і адкладэнне некритычных кліентскіх компонентаў, а не затрымка апдэйта.
Ключовыя выводы
- Чытайце
e.submitterбезпосередзьва ў працоўнікахonSubmit; дзеяння формы ўжо маюцьnameіvalueкантрола. - Заменіце ручныя абэртчыкі
fullscreenchangeнаonFullscreenChangeіonFullscreenError, а таксама обрабатывайце абэртаную прамісу зrequestFullscreen(). - Вызовы
startTransitionможаць тепер выконваныцца ў разных фрамах, таму групавайце апдэйты, якія павінны показацца разам. onResetўжо выконваецца пасля аўтаматычнага скасавання дзеяння, а режым Strict Mode тепер двацічы вызывае эфекты пад час гідратацыі, таму зноў пераканайцеся ў правільной чысткі эфектаў.- Некалькі давялеглых багоў, уключаючы застрэленыя значэння
useDeferredValueі старыя значэнняuseEffectEvent, былі выправлены; пераканайцеся, чы не можна знікнуць якіясь альтернатывы ў вашам кодзе.
react-dom на кліянцы; функцыі, якія раней былі пазначаны як додатковыя, тепер уключаны ў стабільныя версіі.