Осмысление Redux

Когда я только начал изучать React, я помню, как читал много статей о различных связанных с ним технологиях.

В частности, выделялась эта одна статья. В нем упоминается, насколько запутана экосистема и как разработчики часто чувствуют, что они должны знать ВСЕ экосистему, прежде чем использовать React.

И как человек, который использовал React ежедневно в течение последних 8 месяцев или около того, я могу определенно сказать, что я все еще едва поверхностно понимаю, как работает вся экосистема!

Но время, потраченное на React, дало мне некоторое представление о том, когда и почему может быть целесообразно использовать другую технологию - Redux (вариант архитектуры Flux).

Вы будете знать, когда вам понадобится Flux. Если вы не уверены, нужно ли оно вам, оно вам не нужно.

Эта цитата взята из той же статьи, которую я упомянул выше, и в значительной степени резюмирует то, что я думаю о Redux.

Я определенно не видел необходимости в Flux изначально, и мне кажется, что вы можете создавать продуктивные приложения, просто используя React. Но я начинаю понимать преимущества Redux.

В этом сообщении в блоге я расскажу, почему я думаю, что Redux имеет смысл, и дам визуальную аналогию для лучшего усвоения различных частей Redux.

Почему я думаю, что Redux имеет смысл

Вот как я бы описал свое использование React за последние 8 месяцев:

  1. Насколько это возможно, сосредоточьтесь на написании функциональных компонентов без сохранения состояния, поскольку эти компоненты чистые (т. Е. При одинаковых значениях входных свойств всегда будут возвращать одно и то же представление пользовательского интерфейса). Чистые компоненты легче рассуждать и тестировать.
  2. Всякий раз, когда мне нужно было включить состояние в мои компоненты, я внимательно спрашивал себя, какой компонент должен фактически управлять этим состоянием. Должен ли он быть непосредственным родителем определенного компонента без состояния? Или он должен быть выше в иерархии, поскольку он может понадобиться и другим компонентам без сохранения состояния?
  3. Ответ на вышеуказанный вопрос - очень важный шаг в разработке приложений React - на самом деле, даже если вы используете Redux (или какой-либо другой вариант Flux), вам все равно нужно знать ответ на этот вопрос, потому что он определит, какие компоненты будут продвигаться в контейнеры Redux (например, интеллектуальные компоненты).
  4. По мере того, как мое приложение росло, я заметил, что по всему приложению разбросано множество компонентов, которые управляют своим собственным состоянием. Я также заметил, что мне нужно было время от времени проводить рефакторинг, потому что несколько компонентов должны были иметь доступ к одному и тому же состоянию. Типичный паттерн, который я видел, заключался в том, что мне нужно было переместить состояние вверх по иерархии компонентов, чтобы родительский элемент этих компонентов с состоянием теперь управлял этим конкретным состоянием.
  5. Поскольку я все чаще и чаще сталкивался с этим шаблоном, я начал спрашивать, не лучше ли переместить все состояние компонента в верх иерархии компонентов, а затем просто передать определенную часть состояния к тому компоненту, который в нём нуждается. Причина, по которой я подумал, что это будет полезно, заключается в том, что, помимо самого верхнего компонента, все остальные подчиненные компоненты могут быть без сохранения состояния (предостережение: чаще всего вам, вероятно, все равно понадобится чтобы некоторое состояние пользовательского интерфейса управлялось конкретным компонентом).

Drumroll, пожалуйста - представляем Redux

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

React - это UI-библиотека. Он должен иметь дело только с рендерингом пользовательского интерфейса с учетом определенного набора данных. Если данные изменяются, они должны повторно отрисоваться, чтобы сформировать новый пользовательский интерфейс.

Однако по мере роста моего приложения я начал повсюду разбрасывать состояние и бизнес-логику. Это затруднило повторное использование компонентов, и мои компоненты стали действительно толстыми.

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

Я не эксперт по Redux, но вот как я визуализирую его различные части

Когда вы впервые изучаете Redux, вам необходимо понять несколько ключевых концепций: хранилище, действия / создатели действий и функции-редукторы. Официальная документация великолепна, и я настоятельно рекомендую ее прочитать и поиграть с кодом. Мои примеры, приведенные ниже, будут иметь гораздо больший смысл, если вы сначала прочтете документацию!

Я визуально обучаюсь, поэтому вот как я объяснил себе эти концепции:

Магазин - баскетбольное кольцо

В Redux все состояние вашего приложения управляется одним хранилищем. Вы можете рассматривать хранилище как объект JavaScript, где каждый ключ представляет собой определенный фрагмент состояния приложения, которое вы хотите отслеживать, а каждое значение является соответствующим значением для этого конкретного фрагмента состояния.

Вы можете спросить, какое отношение имеет магазин к баскетбольному кольцу? Что ж, если вы позволите мне немного гибкости в моей аналогии, точно так же, как вы можете набирать очки в баскетбольном матче, стреляя мячом через обруч, единственный способ изменить состояние вашего приложения в Redux - это иметь Действия (см. Ниже ) пройти через Магазин.

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

Действия - баскетбольный мяч

Мы упоминали Действия выше, не объясняя, что они собой представляют, но это просто POJO (простые старые объекты JavaScript). Вот пример кода, как они могут выглядеть:

Думая о своем приложении в целом, вы хотите придумать все различные действия, которые может выполнять ваше приложение.

Важно отметить, что действия являются декларативными - они описывают, что вы можете делать в своем приложении, но не то, как это делать. Действия - это чисто данные!

Повторюсь, для того, чтобы ваше приложение изменило свое состояние, вы должны «выстрелить» своим действием «через» магазин :)

Редукторы - тренер и игроки в команде

В предыдущем разделе мы сказали, что действия описывают различные функции вашего приложения. Однако они не описывают, КАК действия изменяют состояние вашего приложения. Здесь в игру вступают редукторы.

Редуктор - это чистая функция, которая принимает текущее состояние и действие как два своих входа, а затем выводит следующее состояние.

Важно, чтобы восстановители были чистыми и не имели побочных эффектов. Каждый раз, когда вы предоставляете одни и те же входные данные, вы всегда должны получать один и тот же результат. Что в этом круто, так это то, что - учитывая начальное состояние и список действий - вы будете точно знать, как результирующее состояние должно выглядеть после каждого действия.

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

Следуя моей аналогии, вы можете рассматривать общий комбинированный редуктор как тренера, а меньший редуктор - как игроков. Как только действие «проходит через» магазин, комбинированный редуктор «улавливает» действие и «передает» одно и то же действие каждой из меньших функций редуктора. Затем каждая меньшая функция-редуктор проверяет действие и определяет, хочет ли она изменить ту часть состояния приложения, за которую она отвечает. И если это произойдет, это создаст новое состояние.

Вот код:

Связывание свободных концов

На этом этапе у вас может возникнуть резонный вопрос: что происходит после того, как каждая подчиненная функция-редуктор создает свое следующее состояние?

После того, как каждая функция вспомогательного редуктора создает соответствующее следующее состояние, создается обновленный объект состояния приложения, который сохраняется в хранилище. Помните, что хранилище - это единственный источник истины для состояния приложения, поэтому после каждого действия, выполняемого редукторами, создается новое состояние, которое сохраняется в хранилище.

Вам также может быть интересно, как мы запускаем процесс - как именно изначально создаются действия?

Redux вводит еще одну концепцию под названием Action Creators, которые представляют собой функции, которые производят и возвращают действия. Эти создатели действий подключаются к компонентам React, поэтому, когда пользователь взаимодействует с пользовательским интерфейсом, вызываются создатели действий и создают новые действия, которые отправляются в магазин.

Заключение

Цель этого поста - не научить вас тонкостям Redux, а, скорее, помочь большему количеству наглядных учеников получить удовольствие от наблюдения за тем, как различные части Redux взаимодействуют друг с другом. Надеюсь, этот пост смог пролить больше света на это!

Примечание. Я недавно наткнулся на это видео Дэна Абрамова, где он упоминает одну из проблем, которые я заметил, когда просто использовал React без Redux. По сути, вы передаете множество свойств от промежуточных компонентов к дочерним, даже если они не нужны этим промежуточным компонентам. Это отличное видео, объясняющее мотивацию Redux и компонентов более высокого порядка в React!