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

React 19.3 – за межамі загадак: аплікант, параметры Fullscreen і размах пакета

Практычны ўзлёт пра менш значыцьвенныя змены ў React 19.3: атрыбут submitter, прызнакі канферэнцыі fullscreen, паралельныя пераходы, функцыя onReset, значныя практыкі багоў і рэальнае збільшэнне размеру.

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

Крыніцы: Everything Else in React 19.3: submitter, Fullscreen Events, Independent Transitions, the Bug Fixes and the Bundle Cost

У прытаках даўнейшых версіяў часта выделяюцца калькі найважлівейшых функцыяй, і 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 &amp; 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 &amp; 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 ).
  • Статус формы быў скасаваны, калі змяніўся несвязанный стан (#34075).
  • Дапрыямкі, якія выкарыстоўваюць токены CSP, апынуліся з паведамленням пра неправильную несувязанасць гідратацыі для атрыбутаў nonce (#37030).
  • Была выправлена проблема з керуваннем фокусам для элементаў, якія былі делегаваны, і для тых элементаў, якія вже мелі фокус (#36010).
  • Былі выправлены два збою, вызваныя элементамі <ViewTransition>: адні ў Mobile Safari (#35337), другі — калі яны выкарыстоўваліся разам з SuspenseList (#35520). Гэта мае значэнне, якщо вы прыменялі эксперыментальныя версіі.
  • У частцы Server Components функцыя 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, былі выправлены; пераканайцеся, чы не можна знікнуць якіясь альтернатывы ў вашам кодзе.
  • Бюджет апоўшацьваеся прыблізна на 16 kB у стані gzip для react-dom на кліянцы; функцыі, якія раней былі пазначаны як додатковыя, тепер уключаны ў стабільныя версіі.