Зберагаць хукі без галавы: аддзеленне падчынення апштоваў ад ўражанняя іх на экране
Дазвольце даклэ расказаць, чаму хук React, які вяртае JSX, адчыняе частку вашага дрэва компонентаў, спаявае поведзенне з візуальным выказвам, і як перерабіць його так, каб умоžніць выкліканне статаусу.
Спецыяльныя хукі чудова падходзяць для сховання деталей рэалізацыі, але інодзе яны сховваюць занадто много. Хук, які вяртае готовы JSX, здаецца зручным у месцы вызыву, але ён перашырвае частку вашага дрэва UI за API, які выглядае як звычныя даны, і прыўязвае поведзенне да аднаго конкрэтнага формата прадставлення. Ужываючы типовы хук для апавешчэнняў з редактара панелі керування, у гэтым артыкуле показана, як распазнаць такія проблемы, як перэабрабатваць код на хук без візуальнага элемента, і што вы зарабатваеце завдяк калькам дадзеных.
Хук для апавешчэнняў, які сам стварае свой UI
Апавешчэнняя ідеальна падходзяць для хука: ў яных трэба стан, можліва таймеры, функцыі для закрыцьця і певная система абробкі запытанняў. Ось як такі хук выкарыстоўваецца:
const {
showSuccess,
showError,
alerts,
} = useAlerts();
return (
<>
{alerts}
<Dashboard />
</>
);
Ужо ёсць пазнакі, што ўсё не так. Функцыя alerts проста дадаецца безпосередна ў JSX, што сильна натыкае на тое, што гук вяртае элементы, якія ўжо адрасаваны. Гук адпаведае за повадкі, таму што ён вяртае showSuccess і showError, а таксама, вочымае, і за выгляд апавяшэнняў.
Рэалізацыя гэта падтверджае:
const useAlerts = () => {
const [alerts, setAlerts] = useState<Alert[]>([]);
const showSuccess = (message: string) => {
// update state
};
const showError = (message: string) => {
// update state
};
...
return {
showSuccess,
showError,
alerts: (
<AlertContainer>
{alerts.map(({ id, type, message }) => (
<Alert
key={id}
type={type}
message={message}
/>
))}
</AlertContainer>
),
};
};
useAlerts керуе станам апавяшэнняў, функцыямі, якія яго зменшаюць, і самым маркапом: кантейнерам AlertContainer і адным элементам Alert на кожную ентрыту. Межа між логікай і візуальным адображэнням зникла.
Чаму вярнуты JSX ўводзіць у глухар
Ён выглядае як даны
З боку корыстніка няма нічога, што бы падсвечала, што alerts ўжо ёсць паддрэва React. Ён знаходзіцца рыле двух функцый, мае множынны назыв, які натыкае на думку пра масэвую структуру даных, і можа легка быць сплутаны з станам:
const {
showSuccess,
showError,
alerts,
} = useAlerts();
Едынны спосаб з’ясавіць правду — адкрыць сам хук.
Ён закрывае вачэнне ад таго, звядзе кампаненты беруцца
Дрэва кампанентоў ценяцца часткова таму, што яны паказваюць, звядзе кожны элемент інтэрфейсу бераецца. Калі адглядаеш панель керування і бачыш гэта:
{alerts}
<Dashboard />
ты не можаш з’ясавіць, што атрыбутуе alerts, якія кампаненты ён адображае, на які контэкст чы пропсы залежнае гэтыя кампаненты, чы і як ў яных выконваецца стайлізацыя, не зайдоўшы ў сам хук. Частка дрэва была пераместжана за ўсё, што выглядае як функцыя керування.
Ён спаявае два незв’язаныя рашэння
Хук большэй не можа выклікваць адзначэння без адночаснага выбору компонента для адзначэння. Кожны экран, які выкарыстоўвае логіку адзначэння, успадковуе ўбранне адзначэння, незалежна ад таго, чы рэальна яго наявнасць. Не існуе тэхнічных прычын, чаму гэтыя два аспекты павінны існаваць разам.
Рефактораванне да безгалоўнага хука
Хук павінен кераваць станам і операцыямі над яным, і нічым больш. Неабяжна выклікваць первісны список адзначэння разам з дзеяннямі, укладаючы таксама спосаб развярнуць адзначэння:
const {
alerts,
showSuccess,
showError,
dismissAlert,
} = useAlerts();
Потым неабяжна атрыбутаваць адзначэння ў компоненте, дзе і так ведзёцца атрыбутаванне:
<AlertContainer>
{alerts.map(({ id, type, message }) => (
<Alert
key={id}
type={type}
message={message}
onDismiss={() => dismissAlert(id)}
/>
))}
</AlertContainer>
<Dashboard />
Тепер дрэва компонента зноў стае ясным. Кожны, хто яго чытае, бачыць AlertContainer, адну елемент Alert на кожную запись, пазначаную id, і прыемнік для развярнуць, падключаны да хука. Ролі хука ўсё такі ж ясныя: ён забезпечвае стан і дзеяння.
Чы ўсё гэта дадатковае кода являецца проблемай?
Компанента, яка выконвае задачу, дзейсна стае дыяўальней, і гэта нормальна. Існуе спакуса доўняваць API так, каб вызывач практычна нічога не пісаў, але такая доўневанне можа стерць корыстную архітэктурную інфармацыю. Пяць ліній коду з явным складам лёгча падтрымаць, чым адна {alerts}, якая маскіруе цэлы фрагмент інтерфейсу.
Якщо калькулі з’яўляюцца на кансольных экранах аднакова, вы все раве можетэ узбегнуць павтарэння, не вяртаючы інтерфейс у хук: створыце компанент AlertList, який будзе прымаць alerts і onDismiss як параметры, або такі, який сам вызываць хук. У будзь-якім случае выкарыстоўванне будзе вестыцца ў компанете, якая є видным элементам у дрэве і можа быць прааналізаваная, запам’ятованая і пераблактаваная як і любая іншая компанета.
Магчыма падзеяванне
Калі JSX ўжо ўбудована, павторны выкарыстоўкі ўзорку поведання прымусвае павторны выкарыстоўкі ўзорку візуальнага адображэння, чыяй правядлівасці не павінна завжды быць. У версіі без галавнага элемента арэя паведамленняў, банер унутранічна і мобільны лейаут можу дзеліць абоўсюды той самы цикл жыцця паведамлення, адображаючы яго розна. Компонент паведамлення таксама можа развівацца самостойна, не паўтараючы код хука, які керуе часам і адключэнням.
Связаная прыемка: хуки, якія вяртаюць компоненты
Блізкі роднік гэтага проблемы — хук, який визначае і вяртае функцыю компонента, напрыклад const AlertView = () => ... у тэле хука. Паколькі ў кожны раз пад час адображэння ствараецца новая функцыя, React кожны раз бачыць новы тип компонента і дэактывае, а потым занова актывае той паддрэс, стіраючы його внутранізны стан і DOM. Вяртанне элементаў, як гэта робіць первісны хук, запобегае гэтаму конкрэтнаму багу, але вышэй описаная проблема з дыяграмай застойва застаецца.
Галоўныя вывары
- Калі з хука выходзіць JSX, гэта зазвычай значыць, што два абавязкі былі з’едыненыя ў адну абстракцыю.
- Значэнне, якое вяртаецца і выглядае як даны, але на самай працэ ёсць UI, закрывае частку дрэва компонентаў ад чытачаў.
- Залічвайце хуки без галавы: вяртайце стан і дзеяння, а рэндаруйце ў компонентах.
- Для спяльнай прэzentацыі выдзельце компонент у замяне на вяртанне маркапа з хука.
- Працюючы з такім хукам, задайце адна запитанне: якая прычына ў гэтага хука вырашваць, як будзе выглядаць UI?