Естественный путь разработчика React - создать несколько игрушечных приложений с использованием идиоматического React с компонентами классов с отслеживанием состояния и несколькими функциональными компонентами. А затем он или она переходит к более централизованной системе управления состоянием, такой как Redux или React context.

Прямо сейчас я нахожусь на этапе создания приложения в своем рабочем месте с React, и я на собственном опыте понял важность управления состоянием приложения в одном месте. Итак, я начал изучать Redux. Это было непросто из-за новизны шаблонов проектирования, которые наложил Redux, таких как flux и pub-sub. Тем не менее, мне удалось обрести некоторую уверенность в арене Redux и я просмотрел несколько руководств по response-redux. Затем я увидел функцию connect. Что каким-то образом берет наш компонент и дает ему доступ к состоянию 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 у нее будет доступ к переменной object из своей собственной области видимости. Однако у него не будет доступа к переменным name и verb. Итак, он пойдет вверх по цепочке областей видимости, чтобы найти указанные переменные. В этом случае функция c получит доступ к переменной verb по ее области закрытия, которая была определена ее внешней функцией , b. Однако функция b не имеет переменной name, определенной в ее области действия. Следовательно, function c пойдет еще на один шаг вверх в цепочке областей видимости и найдет переменную name, определенную в области закрытия function 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 или Any UI framework, например Material UI. Итак, лучшее решение - фактически распространить полученный реквизит HOC, как показано ниже.

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

Хорошо, на этом статья подходит к концу. Я знаю, что нелегко осмыслить все эти способы функционального программирования, но как только вы поймете это, удовлетворение может быть огромным :)

Я здесь вообще не говорил о Redux или функции Connect, и мы подойдем к этому в самом конце, где все будет хорошо сочетаться. Следующая статья будет посвящена контексту React и другому расширенному шаблону проектирования React, называемому реквизитами рендеринга. Быть в курсе!