Естественото пътуване на разработчика на React е да изгради няколко приложения играчки, използвайки идиоматичен React с компоненти на клас със състояние и няколко функционални компонента. И след това той или тя преминава към по-централизирана система за управление на състоянието като Redux или React контекст.

В момента съм във фазата на изграждане на приложение в моята работна практика с React и от първа ръка разбрах важността на управлението на състоянието на приложението на едно място. И така, започнах да изучавам Redux. Което не беше лесно поради новостта на дизайнерските модели, наложени от Redux, като flux и pub-sub. Въпреки това успях да спечеля известна увереност в Redux arena и гледах няколко урока за react-redux. Тогава видях функцията свързване. Което по някакъв начин взема нашия компонент и му дава достъп до състоянието Redux. Но това, което ме заинтригува най-много, беше начинът, по който се наричаше, като connect(mapStateToProps, mapDispatchToProps)(component).

Това очевидно изглеждаше като много странен начин за извикване на функция и затова реших да разбера основната архитектура на функцията. Въпреки това, по време на моето пътуване открих, че включва няколко усъвършенствани модела на React и известни познания в затварянията на JavaScript, за да го оценя напълно. Затова реших да напиша поредица от статии, която бавно се надгражда до изграждане на функцията за свързване от нулата. И така, в тази първа статия ще разгледаме затварянията на JavaScript и след това модела на React компонент от по-висок ред (HOC), който използва затваряния.

JavaScript затваряния

Затварянията често се смятат за плашеща тема, която е по-трудна за разбиране. Което според мен се дължи на начина, по който често се преподават в уроци и книги. Затварянията се обясняват чрез фиктивни примери, като например по-долу, а реалните сценарии за употреба никога не се обясняват.

Така че, ние също ще се опитаме да разберем затварянията от горния фиктивен пример и по-късно ще разгледаме как се използват на практика с HOC.

Накратко, затварянията са функция в JavaScript, където, когато функция е дефинирана в друга функция, вътрешната функция съхранява променливите на външната функция в своя обхват.

Преди да прочетем дефиницията отново втори път, нека да видим какво се случва, когато изпълним външната функция. Когато извикаме външната функция, тя връща вътрешната функция и се изважда от стека за извикване. Което означава, че променливите, които са били дефинирани в неговия обхват, ще бъдат загубени. Ако се опитате да регистрирате в конзолата променливата b, тя ще даде референтна грешка, както се очаква.

Добре, на този етап всичко е наред. Сега обаче нека запазим върнатата вътрешна функция в променлива.

Ето!, извикването на вътрешната функция ни върна очаквания резултат. Но променливата b не е дефинирана в глобалния обхват и очевидно не е дефинирана в обхвата на вътрешната функция. Следователно, важното нещо, което трябва да разберете, е, че вътрешната функция има достъп до друг обхват, наречен обхват на затваряне. Което запазва състоянието на външната си функция, дори след като е извадено от стека за изпълнение. И точно това поведение на JavaScript ни даде толкова много приложения и ни даде възможност да използваме функционални модели на програмиране като къри. В тази поредица от статии ще разгледаме няколко примера от реалния свят, при които се използват затваряния.

Нека разгледаме малко по-усъвършенстван пример, за да изясним ясно тази концепция.

Горното връща функция, наречена b, която от своя страна връща друга функция, наречена c. Нека направим това стъпка по стъпка. Когато за първи път извикаме функция a, тя ще върне функция b и ще бъде извадена от стека за извикване. И мога да запазя тази междинна функция (b) в друга променлива или да я извикам отново веднага след извикване на a (което означава, че всъщност ще извикаме функция b) и запишете финалната, функция c в променлива.

Можем да видим, че функцията cе върнала очаквания резултат. Това, което се случи тук е, че когато функцията cсе извика, тя ще има достъп до променливата обектот собствения си обхват. Той обаче няма да има достъп до променливите, име и глагол. Така че ще се изкачи нагоре по веригата на обхвата, за да намери споменатите променливи. В този случай функцията cще получи достъп до променливата verb чрез нейния обхват на затваряне, който е дефиниран от нейната външна функция, b. Въпреки товафункция bняма променливаиметодефинирано в своя обхват. Следователно функция cще отиде още една стъпка нагоре във веригата на обхвата и ще намери променливатаиме,дефинирана в обхвата на затваряне на функция b. >

Едно от най-добрите обяснения на затварянията, веригата на обхвата и стека за изпълнение е дадено в това видео.

https://www.youtube.com/watch?v=Nt-qa_LlUH0

Моля, прегледайте го, за да разберете затварянията по-ясно. Това обаче е достатъчно за нас точно сега, за да се потопим в HOC модела в React.

Модел на компонент от по-висок ред в React

Сега, след като сме намокрили краката си със затваряния, нека да разгледаме модел, където те могат да бъдат използвани в React. Според React Doc,

Компонент от по-висок ред (HOC) е усъвършенствана техника в React за повторно използване на логиката на компонента. HOC не са част от React API, сами по себе си. Те са модел, който произлиза от композиционната природа на React.

Важното нещо, което трябва да разберете тук е, че HOC не са точно характеристика на React и се използват главно за намаляване на повторяемостта на кода и композиране на подобни функции в компонент от по-висок ред над нашия базов компонент.

Нека се опитаме да разберем това, като разгледаме прост пример. Създадох прост компонент ToDo и компонент Goal, чиято единствена функционалност е да създаде елемент и да го изпрати в DOM.

Поддържах кода доста прост и за двата компонента. Всеки от тях има два обекта на състояние, където един обект на състояние обработва входния елемент, а другият обект на състояние избутва входна стойност към масив, който използваме за показване в списък. Очевидно е, че и двата компонента споделят подобен вид логика и подобен вид състояния. И така, нашата цел в тази глава е да намерим начин да съставим това състояние и поведение (манипулатори за щракване) в един компонент

И така, нека започнем с бебешки стъпки.

Можете да видите, че създадох нов файл, наречен WithItemState, който връща функция. Важното нещо тук, което трябва да забележите, е структурата на тази функция. Той приема компонент на React като аргументи връща друг компоненткойто обгръща този предаден компонент. Отделете време да прочетете тази функция няколко пъти и да разберете нейната структура.

В нашия компонент Todo импортирах нашата функция WithItemState и когато експортираме нашия компонент, просто извиках функцията от по-висок ред с нашия компонент.

Нека разберем какво ще се случи тук. Когато програмата стартира, WithItemState(Todo) ще бъде извикана незабавно и ще изплюе нашия компонент Todo, сега обвит в друг компонент. Основните функционалности на компонента Todo ще бъдат абсолютно същите и ако следвате заедно с мен, ще видите, че нищо не се е променило в изхода. Направих подобно нещо и в компонента Цели. Добре, сега съм сигурен, че сигурно се съмнявате в самата причина да направите това, тъй като ние просто направихме нашата програма много по-сложна, отколкото трябва да бъде.

И така, направих необходимите промени в нашия компонент от по-висок ред. Можете да видите, че създадох общо състояние, за да се справя с всеки вход, независимо дали може да са задачи, цели или нещо друго. И също така създадох някои манипулатори на събития и ги предадох на вътрешния компонент като подпори. Можете да мислите за това като за машина, в която, когато поставите торта в нея, тя ще изплюе тази торта с глазура и черешка отгоре. Но нищо няма да се случи с основната торта.

Добре, така че сега, след като добавихме всички тези подпори към компонента, прехвърлен към нашата функция от по-висок ред, нека видим как да ги използваме в нашия Todo компонент.

Нашият код стана много по-чист, нали? И така, по същество превърнахме нашия компонент със състояние в тъп компонент без състояние. Подобно нещо може да се направи и за компонента цели. И така, това са компонентите от по-висок ред. Те приемат компонент и връщат опакован компонент, който обработва състоянието и се грижи за потребителския интерфейс или бизнес логиката.

Сега, след като видяхте HOC, къде идва затварянето? Както споменах преди, когато програмата стартира, функцията от по-висок ред се извиква незабавно и изскача от стека и ако JavaScript нямаше затваряния, нямаше да може да използва предавания компонент, тъй като не беше в неговия обхват. Разбира се, не е нужно да знаете това, за да използвате HOC, но разбирането на основните основи винаги е полезно в трудни ситуации.

Има още едно важно нещо, което трябва да знаете за HOC. Което е как да предаде собствените си подпори на вътрешния компонент. Нека видим това в действие.

Можете да видите, че в нашия компонент за приложение току-що импортирах и монтирах компонента Todo и Goals. Въпреки това, аз също предадох заглавие на тези компоненти. Това, което трябва да разберете тук е, че реквизитите, които ще бъдат предадени на нашите компоненти, всъщност се предават надолу към обвития компонент от по-висок ред, а не към самия наш вътрешен компонент. Така че, за да предаваме това на вътрешния компонент, трябва да направим нещо подобно,

Ние основно сме предали опората на заглавието, получена от компонента от по-висок ред, до нашия вътрешен компонент. Където вътрешният компонент може да го използва като просто още една опора. Това решение обаче не е мащабируемо, тъй като е трудно да се предвиди броят на подпорите, които ще бъдат предадени, тъй като в наши дни обгръщаме всичко с неща като Redux, Context или всяка UI рамка като Material UI. Така че, най-доброто решение е действително да разпръснете получените подпори HOC, както е показано по-долу.

В този случай не е нужно да следим подпорите в HOC и нашия компонент, тъй като всичко се предава така, както е.

Така че тази статия приключва тук. Знам, че не е лесно да обмислим всички тези начини за функционално програмиране, но след като разберете, удовлетворението може да бъде огромно :)

Тук изобщо не съм говорил за Redux или функцията Connect и ще стигнем до това в самия край, където всичко ще пасне добре. Следващата статия ще бъде за контекста на React и друг усъвършенстван шаблон за проектиране на React, наречен render props. Останете на линия!