Шаблоны дизайна у React: ад з класічных OOP да сучасных хукав
Пасвячаецца таму, як класычныя шаблоны програмнага забэрвання, такія як Singleton, Factory і Observer, прыменяюцца ў React, а таксама шаблонамі, спецыфічнымі для React, такімі як HOC-ы, Hooks і складныя компоненты.
Багато разработчыкаў, якія толькі пачынаюць працаваць з React, не адразу усвядомляюць, што шаблоны дизайна можна застаўляць і паza системамі бэкенду. Часта выдаўаецца, што гэтыя концэпцыі стосуюцься толькі архітектуры сервера, пры чым пазней, будучы професійнымі разработчыкамі фронтэнд-зялёз, вы адкрываеце, што багатыя з гэтых шаблонаў вжываюцца ўжо інстынктыўна, без якога-лібо свядомага апісання.
Шаблоны дизайна — это, по суцэйству, перавераныя, можна-повторна вжываемыя шаблоны для рашэння часта павтараючыхся проблем, якія з’яўляюцца ў програмных проектах. Калі вам трэба, каб ваш код заставаўся арганізаваным, добра структураваным і логічна з’ўязанным, гэтыя шаблоны ствараюць для вас план дзеяння. Яны выступаюць як кодаваныя найкращыя практыки, якія паднімаюць якасц вашага коду і прарабляюць тым часам, які ён заставаецца падтрымуемым.
Сярод найбольшых прыямоў, якія даеяць шаблоны дизайна, — можлівасць павторнага выкарыстоўвання, прыемнасць адтрымкі, масштабаванне, а таксама падыху ў скорасці та эфектыўнасці. Перш чым перейść да шаблонаў, спецыфічных для React, варта пераглянуць фундаментальныя шаблоны інжынеріі програмнаў, якія існуюць ўжо да часаў стварэння фронтенд-фрамворкаў.
Класычныя шаблоны інжынеріі програмнаў
Это шаблоны, якія існуюць незалежна ад конкрэтнага языка і шырока прыменяюцца ў об’ектно-орієнтаваных та функцыянальных парадыгмах. Сам React внутршняя часткаю апыраецца на калькі з іх, а інжынеры, якія працуюць у кодбазе React, выкарыстоўваюцы іх для арганізацыі стану, коордынацыі залежнасцяў у жыцёвым цикле між компанентамі, зменшэння розмеру пакета та падтрымкі зрозумеласці складнай логіки UI.
Шаблон Singleton
Этты шаблон гарантуе, што клас або об’ект мае роўна адну інстанцыю працэса выконання аплікацыі, а таксама з’яўляе едын пункт глобальнага доступу да той інстанцыі.
У фронтэндзе шаблон Singleton ўжыткавы для керування спільнымі ресурсамі — такімі як цэнтрызаваныя хранілішча стану, об’екты налаштавання для всій аплікацыі, інстанцыі для стазісування аналітыкі чыста едын кліент API.
// Singleton API Service
class APIClient {
constructor() {
if (APIClient.instance) {
return APIClient.instance;
}
this.baseURL = "https://api.example.com";
APIClient.instance = this;
}
fetchData(endpoint) {
return fetch(`${this.baseURL}${endpoint}`).then(res => res.json());
}
}
// Any module importing/instantiating this gets the exact same instance
const client1 = new APIClient();
const client2 = new APIClient();
console.log(client1 === client2); // true
За дапамогою сучасных ES-модуляў больш не трэба рукамі перагледваць логіку пераканання ў наявнасці інстанцыі — дастатньва проста экспортаваць адны спільны об’ект чыста інстанцыю, каб без дапамогі іншых засобаў атрымаць поведанне singleton.
// apiClient.js
export const apiClient = new APIClient(); // ES modules cache exports automatically
Шаблон фабрыкі
Шаблон фабрыкі визначае інтэрфейс для стварэння об’ектаў, без таго, каб код, які ўсё гэта вызывае, мусіў знать конкретны клас чы функцыю-канстрактар, якая адпавядае за стварэнне таго об’екта.
Этый шаблон становіцца корыстным, калі трэба дынамічна ствараць элементы UI, працаваць з адказамі API, якія можу прыходзіць у розных форматах, або ствараць абстракціі, якія маскуюць разніцу межы платформамі, напрыклад, аб’еднаваючы спосаб працы веб- і мобільных прыстроў з поданнем дадзенняў.
// Button Factory for dynamically rendering UI elements
function createButton(type, config) {
switch (type) {
case 'primary':
return { role: 'btn-primary', label: config.label, onClick: config.onClick };
case 'icon':
return { role: 'btn-icon', icon: config.iconName, onClick: config.onClick };
case 'link':
return { role: 'btn-link', href: config.url };
default:
throw new Error(`Unsupported button type: ${type}`);
}
}
const primaryBtn = createButton('primary', { label: 'Submit', onClick: () => {} });
Шаблон спазыральніка
Это ўсходні механізм, які лежыць у основе слухачаў змян і бібліятэкаў карыстоўвання стану, такіх як Redux, Zustand і MobX. Варта зазначыць, што API Context у React не выкарыстоўвае гэты шаблон.
class EventEmitter {
constructor() {
this.events = {};
}
// Subscribe
on(event, listener) {
if (!this.events[event]) this.events[event] = [];
this.events[event].push(listener);
}
// Publish
emit(event, data) {
if (this.events[event]) {
this.events[event].forEach(listener => listener(data));
}
}
}
// Usage
const store = new EventEmitter();
// Component A subscribes to state changes
store.on('userLoggedIn', user => console.log(`Welcome, ${user.name}!`));
// Login Service triggers event
store.emit('userLoggedIn', { name: 'Sarah' });
Шаблон модуля
Этый шаблон абгортае код у замкнутасць, так што внутраніяя зменны і функцыі застаюцца прыватнымі, а выклікаецца толькі спецыяльна выбраны публічны інтерфейс.
До таго, калі з’явіліся натыўныя модулі ES6, гэта была стандартная тэхніка для падтрымкі чыстасці глобальнага об’екта window і для забезпечэння практычнае прыватнасці змянных у JavaScript.
// Module using IIFE (Immediately Invoked Function Expression)
const ShoppingCartModule = (function () {
// Private variable
const cart = [];
// Private function
function calculateTotal() {
return cart.reduce((sum, item) => sum + item.price, 0);
}
// Public API
return {
addItem(item) {
cart.push(item);
},
getTotal() {
return calculateTotal();
}
};
})();
ShoppingCartModule.addItem({ name: 'Keyboard', price: 100 });
console.log(ShoppingCartModule.getTotal()); // 100
console.log(ShoppingCartModule.cart); // undefined (Private!)
Сэродзеўнія, натывныя ES-модулі з функцыямі import і export сама адмаўляюць кантэкст, таму рэдка патрабуецца ручная стварэння замкнутага прыемніка толькі для таго, каб зберагчы зменныя прыватнымі.
// cart.js
const cart = []; // Private to cart.js file
export const addItem = (item) => cart.push(item);
export const getTotal = () => cart.reduce((sum, i) => sum + i.price, 0);
Шаблоны дизайна компанентаў у React
Наведзеныя нижэй шаблоны спрыяюць рашэнню проблем, унікальных для адрасавання UI, раздзелення логікі межаў компанентамі, калектывнага керавання станам і адвэтырання чрозмернага перадачы пропаў.
У гэтым раздзеле рассказваецца пра следуючыя шаблоны на рэвэле компанентаў:
- шаблон HOC
- шаблон, створаны на адной з хуков
- шаблон складных компанентаў
- раздзеленне на контейнеры і компаненты для адрасавання
- падход з render props
- шаблон UI на адной з тэхналогій AI
Шаблон HOC
Higher Order Component, або HOC, — адна з першых тэхнікаў, якія запропанаваў React для рашэння спакойных задач. Уявіце сценарый, калі пасля таго, як корыстнік схваліў ведэнне статыстыки, неабходна як можно шырэй запусціць адпаведны заўданне як толькі компонент будзе завантажаны. Вбудовванне такой логікі ў кожны компонент сторнака быстра становіцца повтаральным процесам, а ўточненне яе пазней — напрыклад, калі змянюецца SDK для аналітыкі — прыводзіць да проблем з тымчасовым адтвароненням. Патэрн HOC створаны саме для рашэння такіх проблем.
HOC бере існуючы компонент і дадае яму дапаможныя функцыі, без таго, каб сам компонент мусеў ведаць пра гэтыя дапаможныя функцыі. Саме такое аддзеленне є сэрцам гэтага патэрна. Напрыклад, компонент Page залишаецца прыглашаным выключна да відраслівання, тады як його обгортка withAnalytics(Page) адпаведальна за логіку ведэння статыстыки.
У новейшых кодавочных базах гакі здебольшайшага перадалі на сябе гэту адпаведнасць, але шаблон HOC яшчэ часта можна зустрэць у старых проектах на React.
Шаблон гакоў
Мало чаго змяніла React настолькі фундаментальна, як гакі. Адкрытыя ў React 16.8 у 2019 годзе, яны з тэх пачатку сталі стандартным і найпадобнейшым спосабам для напісання большасці сучаснага коду на React.
Шаблон гакоў дазволяе разработчыкам выделяць і пераіснаваць логіку, якая керуе станам, і парадксальны эфекты між компонентамі за дапамогою звычных функцый, эфектыва заменяючы старэйшыя падходы, такія як HOC і render props.
Компаундны шаблон
Компаундны шаблон дазволяе набору компонентов неявна сааверцаваць над спакульным станам і логікай, не патрабуючы ручна перадаваць усё через пропсы. Ён найчастае выклікаецца у складных інтэрактыўных элементах UI, такіх як меню з выпадам, акордыяныя елементы, вікнічкі та меню навігацыі.
Контейнер / Шаблон прыказча
Этот шаблан спрыяе чыстам раздзеленню абавязакоў паляганням на аддзеленні абавязакоў компаненту на два разныя ролі: адна, якая керуе логікай прыемлена, і другая, якая зусім займаецца атрыбутамі інтерфейсу.
Компанент-контэйнер адпавядае за выбір тых дадзенняў, якія павінны быць адразу відобразлены для пользователя. Ён керуе станам, распрацоўвае парадкавыя эфекты і містіць логіку прыемлена.
У сваю чаргу, компанент-прыказч займаецца тым, як гэтыя дадзенні будуць атрыбутамі. Ён проста прымеўляе дадзенні і функцыі-выкліканні через атрыбуты, не зміняючы самых дадзенняў.
Сучасная разработка на React сакрэтная зусім на власных хукіх, а не на раздзеленні на контейнеры і прэzentатывальныя компаненты. У замест на стварэнне спецыяльнага контейнеровага компанента толькі для запрашоўвання дадзеных, можна выявіць гэтую логіку запрашоўвання ў власны хук і вызваць яго безпосередна ў тым компаненте, які яго патрэбуе. Це дазволяе захаваць раздзеленне абавязакоў, а таксама усунуць дадатковыя шары вкладання компанентоў і павтарюваны базовы код.
Шаблон render props
За дапамогою гэтага шаблону функцыя перадаецца як проп у компанент, чым той компанент отримвае контроль над станам і логікай, а рашэнне пра тое, што прадстаўляць, залишаецца за тым, хто яго выкарыстоўвае.
Асновная ідея render props заключаецца у тым, што замест таго, каб компанент-обгорак сам прадстаўляў жорстка заданы UI, ён выконвае свою внутранюю логіку, а пасля вызывае функцыю-проп, каб стварыць выніклы JSX.
У сучасным версіях React спецыяльныя хукі здзейчай вытэплілі ролю атрыбутаў render, якія раней викорыстоваліся для перадачы чыстай логікі данных. Тым не менш, атрыбуты render застаюцься практычнымі, калі компоненту трэба кераваць цэлым паддрэвам, а ў той жа час дазволіць тому, хто яго вызывае, выбраць формат відображэння. Бібліятекі безвыкарыстоўванай UI, такія як React Aria і TanStack Table, паводзяцца этым падходам, каб реалізаваць складную логіку — кераванне доступнасцю, управленьне фокусам і падобныя задачы — не навязваючы конкрэтнага стылю чыраў DOM.
Шаблон UI для AI
Эта пункт ў спісе ёсць супэльна няўялікі. Стварэнне інтерфейсаў, керованых ШІ, чы то чат-ботаў, чы болей загальных інтэлігентных асистэнтаў, выклікае неабходнасць роцэйнай коордынаціі межаў абслугавачаў ШІ у задній часты і реакtywnага шару інтэрфейса. Модель ШІ-інтерфейса па сутнасці заключаецца у з’яўленні звязку межаў большых мовных модэляў у задній часты і реакtywnых кліентскіх інтерфейсаў, каб тыя моглі без проблем адрабатваць дыялогі, стрімаваныя адпаведзі і асінхронную експанатырацыю модэляў.
Адна з цэнтральных ідэй тут — аддзеленыя ўтриманне задней часті і проксі-шара ад кліента. Ёжчы не выклікваць паводаку API-клаўяў і правільна кераваць вычысловым навантажэнням, усі вызывы, якія стосуюцца ШІ, павінны праходзіць через шар на стороне сервера — чымусь у духу Next.js Route Handlers або Node.js API-проксі, які знаходзіцца перед Vite. Вызыванне абслугавачаў ШІ безпосередна з браузера — гэта тое, чаго трэба абсалютна утрымвацца.
Спадневанае чытанне
- Тры типаскріптовыя шаблонамы, якія паспрыягаюць у падборы архітектуры React-праектаў — Дазвольце вам дазнацца, як шаблонамы Repository, Observer і Builder выкарыстоўваюць систему типаў TypeScript для стварэння чыстейшых і лягчэй кераваных кодавых базаў для React і Next.js.
- Тыпаванне React-хуков: useState, useEffect, useReducer і спецыяльныя хуки — Дазвольце вам дазнацца, як правільна задаць типы для useState, useEffect, useReducer і спецыяльных хуков у TypeScript, а таксама калі выкарыстоўванне TypeScript замест звычайнага JavaScript дэйсна прыносіць практычныя выгоды.