Стварэнне ментальнага моделю для React: супрацоўка, стан і хукі
Выявіце прычыны, якія стояць за асновнымі канцэптамі React — супраявленне, компоненты, пропсы, стан і хукі — каб развіць інтуўіцыю замест таго, каб запамятаваць API.
Якщо вы вже вмеете післяваць код, але ўсё ещы не глыбокаў у React — або ж толькі короткаа прабаваліся з яго — гэты матыял напісан саме для вас.
Калі вы пачынаете вучыцца React, бывае спакуса сразу перейсці да useState, useEffect, props, hooks і дужоўкі іншых API. Вы можете засваяць синтаксіс, стварыць праект, які працуе, але так і не зрозумець чаму React паводзіцца так, як паводзіцца.
Мэтай гэтага артыкула є заполненне гэтага прыему.
Уместо таго, каб спрыяць React як скупку API, якія трэба запам’ятаваць, мы будзем дакладна розглядаць прычыны, якія стояць за дизайнам React, і як усе його элементы взаімаўна пав’язаны. Синтаксіс мае значэнне, але ўсё становіцца набагато простыяй, калі вы разумеете, што вадзіцца пад ўсім гэтым. Таму замест таго, каб пачынаць з як пісаць код на React, мы спачатку розберамо як мысліць пра React.
Мы розглянем компаненты, пропсы, стан, атрыбуты, процес выклікаў, прырэштаванне, хукі, проп-дрілінг, контэкст і маршрутызацыю, прабуючы з’ясаваць, як кожная з гэтых ідэй спрыяе рашэнню проблемы, для якой яна была створана. Мэтай не ў тым, каб перыявіць усе API, якія пропонуе React, а ў тым, каб даць вам псіхалагічную модель, якая дапаможае легка разумець гэтыя API, калі вы з імі пазнаёмасе.
Верагодна, вы вядомы, што адной з прычын такой высокай скорасці React є якісь хитры алгорытм. Гэта можа здавацца майже чарамі — як бібліятэка на JavaScript можа так эфектыўна адканаліцаваць інтерфейс?
Гэты алгорытм называецца прырэштаванне, і гэта хорашы пункт для пачатку.
Прырэштаванне — алгорытм, які зрабіў можлівым React
Пораўнанне двух дрэва DOM з нуля і вычысленне мінімальнага набору змян, якія трэба адбыцца, — гэта проблема, якую можна рашыць за апошнія O(n³) часу.
Але як жа, якщо процес супраязду будзе выкарыстоўваць калькольку разумных прыпускаў, а мы дамо яму калькольку падказак пра тое, дзе, верагодна, будуць змяны?
І гэтак! Складнасць знижваецца да прыблізна O(n).
Это значыць, што падход, які наўмовна зайняў бы абоўшчы 31 годзіну на завершэнне, тепер зменшыцца да прыблізна 16 хвілін — выключна за дапамогою прыпускаў і невялікай падказкі з боку разработчыка.
Чакайце… падказкі? Чаму жэсткая ўсе гэтыя падказкі, і як мы іх даўаем?
Няма прычыны варушыцца. Это простае, чым здаецца, і мы скора все раз'яснім.
Пакуль давайце парадзім супраязду выканаць свою роботу на аўтономных правілах і перайдзем да пытання, якое насправды мае значэнне для нас, разработчыкаў.
Але чаму взагалі трэба беспакоўвацца пра React?
Чаму React?
Незалежна адзінай калі хто бы то ні было вам гаворыў, калі вы пераходзите з простых файлаў HTML, CSS і JavaScript да концэпцый, якія называюцца компанентамі, хукамі, пропамі і стаўам, адбываецца справжня псіхічная адаптация.
Паралельна такім фрэймворкам, як FastAPI чыў Go, React можа справдзіцца такім, што выклікае ўжо большыя труднасці для разумэння.
Але справа ў тым, што гэтыя труднасці знаходзяцца на пачатку.
Калі вы почнёте краща разумець прыроду React, вы паведаміцеся, што багато з гэтых на першы погляд страшных концэпцый базуецца на прыгожа простых прынцыпах.
Вам не трэба адразу памяцать всю свою прыкладнасць у галаве.
У замену вы можете сфокусавацца на адзінам компаненте — якія даныя ён прымае, што ў яго трэба стежыць і як павінна адначасова апошневаць яго візуальная выкарыстоўванне.
Гэты распадзены спосаб мышленья якраз і дапамагае ў стварэнні та падтрымцы складных інтерфейсоў. І, востатку, гэта тое, чым насправды карабяцца разработчыкі.
Стварэнне коду, які лёгкая ў стварэнні, розумеўцы, змянэ та падтрымцы. Мета — дапамогчы вам досягнуць гэтага.
Чатыры сталпы React :—
Компаненты
Компанент — это, па суті, функцыя на JavaScript, якая прымее адзін об’ект і вернуе частку UI, напісаную на JSX.
JSX — это расшырэнне сінтаксісу для JavaScript, якое дазволяе вставляць маркап, сэродзейны HTML, працуючы безпосередна ў коде на JavaScript. Стайлізацыю можна адрабатваць за дапамогою звычайнага CSS або бібліятэкі з інструментамі, такой як Tailwind CSS.
Будзь-яны достатна складныя прыклады аплікацый на React, у падсумку, ўсё роўна являюцься сетчаткай компанентаў, якія перадаюць даны адна між іншай.
Спачатку вы моглі нават не стыкаться з React, але вы яшчэ можаце разумець, што робіць кожны компанент.
const data = {
question: "What is React?",
answer: "A JavaScript library for building user interfaces"
};
function FlashCard(data) {
return (
<div className="border rounded-lg bg-gray-50 p-4">
<h2>{data.question}</h2>
<p>{data.answer}</p>
</div>
);
}
Якшто не зважаць на калькі синтаксу, прызначанага спецыяльна для React, то гэта ў сутнасі весь сенс React.
Не так вже й паскудна, правда?
Па сутнасі мы падаём даны ў функцыю і атрыбуеўаемся часткай UI.
Таму, як разработчыку, ваша справжня задача — ствараць чыстыя компаненты, памеркнуўшы на тры пытанні:
- Какія даны ён прыймае?
- Какія даны ён зберагае?
- Як должна змяніцца яго выгляд?
Што можа быць складным у канліку параметраў і калькуляцый? Чаму бы проста не перадаць неабходныя даны і не зберагчы тое, што хочам, у локальных калькуляцый?
У паводзе — але не цалкам.
А што мы маем на увазе пад аднавленням інтэрфейсу? Уявіце двух працавнікаў, якія намагаюцца продаць аднойчы той самы автомабіль, але керавальнік паведамляе пра змяну цены толькі аднаму з іх.
Што будзе з другім працавнікам? Ён продавае автомабіль за старую цену, нічога не паведамлены.
Тепер уявіце, калі керавальнік публікуе аднавленне ў груповым чате, у якім є всі. Зменіўшы цену раз, весь колектыв бачыць гэта адразу.
Гэта, по суты, проблема, якую створана React, каб рашыць.
Калі яка-небудзь інфармацыя, яка вплывае на тое, што паказваецца на экране, змінюецца, усё, што адналежыць да яе — людзі ў нашай аналогіі, компаненты ў React — патрабуе надзеянага спосабу дазнацца пра гэтую змяну і адрасаваць яе. Гэты механізм і ўтварае React.
Props
Чы можна заменіць function User(name, age, city) на function User(name, city)? А як ў разе з function User(city, name)?
Тыповая функцыя, якая выкорыстоўвае параметры па пазіцыі, можа прыймаць толькі адлічымую канстанту аргументаў у пэўнай прыродзе — і памятайце, компанент — гэта проста функцыя.
Тепер уявіце, што вы створылі компанент, який паказвае імя і возраст пользователя, і ён зараз выкорыстоўваецца у 67 разных месцах у вашай базе коду. Тады ваш менеджер папросіць вас таксама паказаць горад пользователя. Адкорэктаванне кожнага з варыянтаў выкарыстоўвання займе гадзіны роботы.
Але што, як функцыя будзе спроектавана так, каб старыя вызовы продолжалі працаваць без змян, а новыя вызовы моглі за бажанням перадаваць дапаможныя даны?
Інструмент, які вам патрэбен, — это адзін об’ект, які выступае заместо спісу параметраў. React называе гэта props.
Props — это проста спосаб, яким React агрупавае всі даны, якія передаюцца компоненту, у адзін об’ект.
/** So a component like this one **/
function User({ name = "Guest", age = 18, city = "Unknown" }) {
return (
<div>
<h2>{name}</h2>
<p>{age} years old</p>
<p>{city}</p>
</div>
);
}
/** Can be used in ways like **/
<User /> /** Guest, 18, Unknown **/
<User name="Pritam" /> /** Pritam, 18, Unknown **/
<User name="Rahul" age={21} /> /** Rahul, 21, Unknown **/
<User name="Priya" age={22} city="Delhi" /> /** Priya, 22, Delhi **/
Станы
Чы хадзеў менеджер дилерскага центра захаваць новую цэну толькі для сябе? Сказаць толькі аднаму продавцу? Обом ім? Чы, можа, апавесціць пра гэта всім у дилерскам центре, включаючы охраннікаў і працоўнікаў па чысткі?
Уявіце коробку для зберагчэння, якую вы заполняеце сваімі рэчамі. Чы можаце вы, проста паглядзевшы на яе ззаўні, з’ясавіць, чы ўсередзіне ў чымсь змянілася? А што, як коробку перасунуць на іншую поліцу? Як мінімум, вы заўважыце, што яе положэнне змінілася, і здагадаецеся, што ў чымсь было.
Тепер уявіце, што гэтая коробка — це механізм, які викорыстоўвае React для зберагчэння значэнняў, якія вы задаеце.
Это значыць, што патрэбна магчымасць перадаці апошніх значэнняў тым, хто ад яных залежыць, а таксама магчымасць для гэтых залежных элементаў дазнацца, калі ў их значэнні з’явілася змена.
const [count, setCount] = useState(initialCount);
Чы сталося вам ранейша зустрэць такі вызыв useState?
Ён передае компоненту два элементы:
- count — тэкучая значэнне state.
- setCount — функцыя, яку вы вызываеце, каб прагледзець апдэйт данага state-значэння.
То што ж не так з тым, калі проста запісаць value = 5?
У React няма можлівасці дазнацца, што ўсередзіне „коробкі“ ўтварылася змена!
Іншымі словамі, вам патрэбен механізм, каб сказаць React: „Гэй, я апдэтуваў гэтыя значэння, можа вам стоіць апшортаваць інтерфейс.“
Ці гэта той натхненнік з ранейшага? Часткова, так і няма.
useState дае вам можлівасць зберагаць значэння, запрашваць яго апдэты і адночасна паведаміць React пра тое, што адбылася змена. Але чаму React патрэбна явная інформацыя?
Таму што інакш вы будете падобацца таму недбаламу менеджэру автосалона.
Памятаеце тую практычную памылку? Менеджэр змяніў цэну, апдэтуваў свой сабскоп таблы і забыў паведаміць іншых. Вы розумней — вы проста вызываеце setCount().
То што на самай працэ ўтвараецца, калі вы вызываеце setCount()?
React берае новы стан, перзапускае рендэрінг компонента, ўбачыць, як павінен выглядаць інтерфейс, а потым паводлі процесу супраўляння выявляе, што насправды павінна зменіцца на экране.
Вы проста паведамілі React, што шта-небудзь зменілася. Супраўляння выявляе што зменілася і шта павінна быть апдэйтавана як наследак.
Hooks
useState() — вбудованая функцыя, якая дазволяе компоненту зберагаць частку стану і дае можлівасць запрашваць яго апдэйты.
Калі стан апдэйтаваецца, можуць выйсці два рэзультаты:
- Новая значенне не паўтарае адрасавання элементаў — React можа яшчэ перзапустіць рендэрінг, але нічога візуальна не змяніцца.
Функцыі, такія як гэтая, якія маюць спецыяльныя можлівасці React, называюцца хукамі. Існуе ўсё больш такіх, пра якія варта знать.
useRef() — корыстны, калі вам патрэбен контейнер для значэння, якое зовсама не мае нічыну з інтэрфейсам.
const count = useRef(0);
count.current++;
Такім чынам, загальны прынцып: якщо змена значэння таксама павінна зменіць інтэрфейс, вжывайте useState(). Якщо трэба зменіць толькі сама значэнне, без жадных наследкав для рэндараўвання, вжывайте useRef().
У яго таксама ёсць другое распашчастае застосоўванне — апісанне рэальных элементаў DOM, але пакуль гэтая канцепцыя вастаткі ўсё.
useEffect() — для ситуацый, калі патрэбна адпрацоўка чагосьці пасля таго, як React завершыў атрыбутаванне інтерфейса.
useEffect(() => {
console.log("Runs after every render");
});
useEffect(() => {
console.log("Runs once after the initial render");
}, []);
useEffect(() => {
console.log("After the initial render and whenever count changes");
}, [count]); /** Dependencies go here **/
useContext() — паглядзіце на дрэво компанентаў, дзе даны, такія як name, павінны праходзіць через калькі слоёў компанентаў, каб дасягнуць глыбока ўвінчанага компанента UserName.
Тепер расшырыце гэта дрэва, дадаўшы калькі паказчыкаў даных, якія праходзяць через компаненты, якія нават не выкарыстоўваюць іх безпасова.
Такі патэрн называецца prop drilling.
API Context у React прыносіць спосаб пераканацца гэтай проблемы: ён дазволяе робіць даны доступнымі ў будзь-якай глыбей дрэва, не прабываючы ручна перадаваць іх через кожны компанент па дарозе.
const ParentContext = createContext(null)
<ParentContext value={money}>
<ChildComponent />
</ ParentContext>
function ChildComponent() {
const money = useContext(ParentContext);
return <p>Money: {money}</p>;
}
Існуе ўжо багато іншых хуків, і варта спробаваць ўжоваць іх самостайна.
Да гэтага момента размова была пра тое, як React арганізуе компоненты і як данні пераходзяць межы ўсіма імі. Але рэальныя прыклады застосоўвання таксама павінны выбраць, якія часткі гэтага інтерфейсу павінны апыляцца пад якімі URL-адрэсамі.
React Router
React дазваляе ствараць інтерфейсы, складзеныя з компонентоў, якіе самы сабе апцэнтуюць, не прымушваючы браузер перазавантажваць усю сторунку. Але рэальныя прыклады застосоўвання зазвычай патрабуюць больш чым аднай сторункі, і гэта паднімае новыя запитанні.
Што будзе, калі ваша прыклад застосоўвання патрабавае калькі разных відзораў?
Возможна, вам патрэбна што-та такое:
/home → Галоўная
/dashboard → Панель керування
/profile → Прафіль
Якщо вы під’єднаеце ўсё гэта за дапамогою звычных тагаў HTML, кліканне на іх спрычынае повную навігацыю ў браузеры. Весь стары адзінак выкарыстоўваецца, і дапыт зноў запускаецца з нуля за новым адресам.
На самай працоўцы вам трэба, каб URL адкрываўся, пакалі React тыхо визначае каторыя компоненты трэба заменіць, не выкарыстоўваючы ўсё іншае.
Самэ гэтаю проблемай займаецца React Router.
Уявіце сабе гэта як шар, які ператварае URL-адресы на компоненты.
Напрыклад:
<BrowserRouter>
<Routes>
<Route path="/home" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</BrowserRouter>
React Router аналізуе текущы URL і відображае той компонент, які даўно з’являецца з ім. BrowserRouter выкарыстоўвае API історыі браузера для керавання навігацыёю на стороне кліента, а Routes выбірае той Route, які найлепша падходзіць да текущага шляху.
Протыма, існуе ўсё жа ўзгоркі, з якімі трэба разабрацца.
Што, як вы не хачэце, каб весь экран пераранжаваўся?
Уявіце сабе такую структуру:
┌─────────────────────────────┐
│ Header │
├──────────┬──────────────────┤
│ │ │
│ Menu │ Page content │
│ │ │
├──────────┴──────────────────┤
│ Footer │
└─────────────────────────────┘
Перыход з /home на /dashboard не должен прыводзіць да таго, каб загалоўкі, бокавая панель чы ўсё нижнее роўнюванне з’являліся і зноў зьнікалі. Павінна меняцца толькі галоўная зона з контэнтам.
Самэ гэта ўмоўленне і створанае для вкладзіных маршрутаў і <Outlet />.
function Layout() {
return (
<>
<Header />
<Menu />
<Outlet />
<Footer />
</>
);
}
Самыя маршруты можна тады вкладваць адні ў іншыя:
<Routes>
<Route element={<Layout />}>
<Route index element={<Home />} />
<Route path="dashboard" element={<Dashboard />} />
</Route>
</Routes>
З такой наладкай Layout застаецца установленым, а React Router заменяе його на той чы іншы дзеціны маршрут, які падходзіць унутро <Outlet />. За інструкцыямі самога React Router, <Outlet /> пазначае месца, дзе аранжуецца падходячы дзеціны маршрут.
index маршрут адаптавае <Home /> пад шлях /, таму калі няма болей спецыфічнага шляху, гэты маршрут становіцца стандартным адзорамом, які паказваецца ў выходным элементе.
Такім чынам, пераход з /home на /dashboard на самай справе не значыць замены цэлага адзорама. Гэта больш супамінае:
"Залейце гэтую частку інтерфейса такой, якая ёй є, а заменіце толькі гэты раздел на той компонент, який належыць да новага маршрута."
Што далей?
Калі вы вжо добра разумеете проблемы, якія павінен рашыць React, і механізмы, якія ён выкарыстоўвае для ўсунення іх, варта пасвятіць час чытанню рэальных кодавых баз, каб пазнаць, як команды з досвідам структуруюць свае прыкладнія.
Шуцьце репазітарый, які паказвае, як можна організаваць і спроектаваць прыкладнэ програма на React у масштабных умовах.
У замест на тое, каб спробаваць асіміляваць увесь кодбазу за адну практыку, выберыце адзін функцыянал і ступіце за ўсіма ярусамі прыложэння. Зверніце увагу, як групуюцца компаненты, дзе бераюцца даныя, як карацца станом і як разныя часткі прыложэння взаемадзейнуюць.
Таксама цікава будзе знаходка прыкладу, який паказвае, як React можа быць сумешаны з Redux у працюючам прыложэнні.
Якщо проект, які вы знаходзіце, ўжо стары, не спрыявайце його патэранам як дабаровым стандартам для React. Усуніце яго замест на тое, каб вывучыць, як вялікае прыложэння можа быць разбіта на часткі і як Redux впіваецца ў гэтую структуру.
Калі вы вучыцеся кераваць тыповай структурой файлаў у React і можете самі ствараць простыя компаненты, наступны крок — вывучэнне стварэння прыложэнняў, якія можаць функцыонаваць у великых масштабах.
Расшырэнне аплікацыі на React прыводзіць да сябе спецыфічных проблем, з якімі трэба справляцца:
- SSR і серверныя компаненты — як змінюецца працэўная супераспэкт, калі частыні аплікацыі выконваюцца на сервере, а не цэлкам у браузеры.
- Кантроль стану — што робіць, калі стан аплікацыі стае занадта великім або шырокаа распраўляецца, так што локальны стан і Context не можу яго эфектыва кантролаваць. Redux — адна з кальколька можлівасцей.
- Зяўленне і кэшаванне дадзеных — як аплікацыі для практычнага вжытку кантролююць індыкаторы завантажэння, обработку абякоў, кэшаванне і падтрымкю сінхроннасці дадзеных кліента з серверам.
- Прыдатнасць — выявленне момента, калі рэндарынг практычна стае бутылочным горлам, і оптымаізацыя ў такім разе, а не прабыванне оптымаізаваць усё з самага пачатку.
Вам не трэба володзець усімі гэтымі тэмамі перад тым, як пачаць ствараць.
Болей практычны падход — пачаць ствараць, зіткнуцца з конкрэтным проблемам, а потым выучыць тую концэпцію чы роўны, якія рашаюць гэтую конкрэтную проблему.
У канцы канцоў, мета выучэння React ніколі не была запам’ятаванне яго API. Це было разумэнне чаму гэтыя API існуюць і як разумець проблемы, якія ўсунуць.
Супаўзяленая літэратура
- Розумеўце кастамных хуків у React: павторны выкарыстоўванне логіки без спяльнага статаусу — Дазволіць вам дакладна зразумець, што такое кастамныя хукі у React, як вони выделяюць та дзеляць логіку з статаусам между компонентамі, а таксама якія распашчытныя памылкі трэба ухіляцца пад час ўтварэння такіх хуків.
- Шаблоны дизайну у React: ад класычных парадактов OOP да савременных хуків — Паспяшае чытальніка аб разліку класычных шаблонаў програмнага забезпечэння, такіх як Singleton, Factory та Observer, і ўпражнення ў React-спецыфічных шаблонах, такіх як HOC-ы, хукі та складныя компоненты.