Упрощение шаблона¶
Redux частично вдохновлен Flux, а самая большая жалоба на Flux — это то, что он заставляет Вас писать много лишнего. В этом рецепте мы рассмотрим, как Redux дает нам выбор, насколько подробно мы хотели бы писать наш код, в зависимости от личного стиля, предпочтений команды, долгосрочном плане ремонтопригодности и так далее.
Экшены¶
Экшены — это простые объекты, описывающее, что происходит в приложении. Они являются единственным способом описать намерение изменить данные. Важно, что экшены, будучи объектами, которые Вы должны отправлять, являются не шаблоном, а одним из фундаментальных принципов Redux.
Существуют фреймворки, утверждающие, что они подобны Flux, но без концепции объектов экшенов. С точки зрения предсказуемости, это шаг назад от Flux или Redux. Если в них нет сериализируемых простых объектов экшенов, невозможно записать и воспроизвести сеансы пользователя, или реализовать hot reloading with time travel. Если Вы предпочитаете изменять данные напрямую, Вам не нужен Redux.
Экшены выглядят следующим образом:
1 2 3 |
|
Общепринято, что экшены имеют поле type
, которое помогает редьюсерам (или сторам в Flux) опознавать их. Мы рекомендуем вам использовать строки (String) вместо символов (Symbol) для обозначения типа, т.к. строки — сериализуемы, а использование символов может усложнить запись и воспроизведение, когда это потребуется.
Во Flux традиционно считается, что Вы должны определять каждый тип экшенов строковой константой:
1 2 3 |
|
Чем это выгодно? Часто утверждают, что константы не нужны, и для маленьких проектов это может быть правильно. Для больших проектов, существует несколько преимуществ определение типов экшенов через константы:
- Это помогает держать наименование последовательным, потому что все типы экшенов собраны в одном месте.
- Иногда лучше видеть все существующие экшены перед началом работы над следующим функционалом. Может такое случиться, что нужный вам экшен уже добавлен кем-то из членов команды, но Вы не знали.
- Список типов экшено, которые уже были добавлены, удалены и изменены в Pull Request поможет каждому члену команды отслеживать объем и реализацию нового функционала.
- Если Вы допустили опечатку при импорте константы, Вы получите
undefined
. Redux немедленно пробросит это, когда будет отправлять такой экшен, и Вы найдете ошибку быстрее.
Выбор соглашений для Вашего приложения остается на Ваше усмотрение. Вы можете начать использовать строки, а позже перейти к константам, а, возможно, еще позже — вынести их в отдельный файл. Redux не имеет ограничений по этому поводу, так что это остается на Ваше усмотрение.
Генераторы экшенов¶
Другое общепринятое соглашение — это вместо создания объектов экшенов в той же части приложения, где эти экшены вызываются, создавать функции, генерирующие их.
Например, вместо вызова dispatch
с агрументом-объектом:
1 2 3 4 5 |
|
Вы можете написать генератор экшенов в отдельном файле и импортировать его в Ваш компонент:
actionCreators.js
1 2 3 4 5 6 |
|
AddTodo.js
1 2 3 4 |
|
Генераторы экшенов часто подвергались критике за свою шаблонность. Что ж, Вы можете их не писать! Вы можете использовать объекты, если чувствуете, что это лучше подходит для Вашего проекта. Однако, некоторые рекомендации по написанию генераторов экшенов вы должны знать.
Допустим, дизайнер приходит к нам после просмотра нашего прототипа и говорит, что надо ограничить число задач тремя максимум. Мы можем реализовать это, переписав наш генератор экшенов на коллбэк, используя redux-thunk мидлвар и добавив преждевременный выход из функции:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
Мы всего лишь изменили поведение генератора экшенов addTodo
, совершенно незаметно для кода, использующего этот генератор кода. Нам не пришлось заботиться о поиске каждого кусочка кода, где происходит добавление, чтобы убедиться, что они работают правильно. Генераторы экшенов позволяют Вам отделять дополнительную логику отправки экшенов от реального выбрасывания этих экшенов компонентами.
Создание генераторов экшенов¶
Некоторые фреймворки, такие как Flummox, создают константы типов экшенов автоматически из описаний функций-генераторов экшенов. Идея состоит в том, что Вам не надо описывать и ADD_TODO
константу, и addTodo()
генератор экшенов. Под капотом такие решения все еще создают константы типов экшенов, но они созданы неявно, так что это новый уровень абстракции, что может привести к путанице. Мы рекомендуем Вам создавать константы типов экшенов самостоятельно.
Написание простого генератора экшенов может быть утомительно и часто заканчивается созданием избыточно шаблонного кода:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
Вы также можете написать функцию, которая создает генератор экшенов:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
Также существуют служебные библиотеки для помощи в создании генераторов экшенов, такие как redux-act и redux-actions. Они могут помочь уменьшить шаблонный код и обеспечить соблюдение стандартов, таких как Flux Standard Action (FSA).
Асинхронные генераторы экшенов¶
Мидлвары (middleware) позволяют Вам внедрять обычную логику, которая обрабатывает каждый экшен перед тем как отправить. Асинхронные экшены — наиболее распространенные вариант применения мидлваров.
Без мидлвара, dispatch
принимает только простой объект, поэтому нам приходится выполнять AJAX-запросы внутри компонентов:
actionCreators.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
UserInfo.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
|
Однако, код быстро становится повторяющимся, потому что разные компоненты запрашивают данные из одной и той же точки входа в API. Более того, мы хотим переиспользовать некоторые части этой логики (например, ранний выход, когда доступны кэшированные данные) из многих компонентов.
Мидлвар позволяет нам писать более выразительные, потенциально асинхронные генераторы экшенов. Это позволяет нам отправлять в качестве экшенов что-то помимо простых объектов и интерпретировать значения. Например, мидлвар может “отлавливать” отправленные промисы (Promises) и обращать их в пару из запроса и успешных/провальных экшенов.
Простейший пример мидлвара — redux-thunk. “Thunk” мидлвар позволяет нам писать генераторы экшенов как “thunks” — функции, возвращающие функции. Это переворачивает управление: Вы будете получать dispatch
в качестве аргумента, так Вы сможете писать генератор экшенов, который отправляется несколько раз.
Запомните
Thunk мидлвар — всего лишь один пример мидлваров. Мидлвар не несет в себе смысл “предоставлять отправку функций”. Он предоставляет Вам возможность отправлять что угодно, что Вы сможете обработать. Thunk мидлвар добавляет специфическое поведение, когда Вы отправляете функции, но это зависит от используемого мидлвара.
Рассмотрим приведенный выше код, переписанный с использованием redux-thunk:
actionCreators.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
UserInfo.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
Так гораздо меньше кода! Если хотите, Вы можете все еще использовать “голые” генераторы экшенов, такие как loadPostsSuccess
, которые Вы бы использовали в контейнере loadPosts
генератора экшена.
В конце концов, Вы можете написать свой собственный мидлвар. Допустим, Вы хотите обобщить шаблон выше и описывать Ваши асинхронные генераторы экшенов следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Мидлвар, который обрабатывает такие экшены, может выглядить так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
|
Один раз вызвав applyMiddleware(...middlewares)
, Вы можете писать все Ваши генераторы экшенов с API вызовами следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
|
Редьюсеры¶
Redux значительно упрощает шаблон Flux-стора описанием логики обновления в виде функции. Функция проще, чем объект, и гораздо проще, чем класс.
Вглянем на это Flux-стор:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
С Redux та же логика обновления может быть описана как упрощенная функция:
1 2 3 4 5 6 7 8 9 |
|
Switch
оператор — это не шаблон. Реальный шаблон Flux абстрактен: необходимо выдавать обновления данных, необходимо зарегистрировать Стор в Диспетчере, необходимо представлять Стор в виде объекта (и другие затруднения, которые возникают, когда Вы хотите получить универсальное приложение).
Печально, что многие все еще выбирают Flux-фреймворк на основе того, диктует ли он в документации использовать switch
оператор. Если Вы не любите switch
, Вы можете использовать функцию, которую мы показывали прежде.
Создание редьюсеров¶
Давайте напишем функцию, которая позволит нам выражать редьюсеры как объект, сопоставляющий типы экшенов обработчикам. Например, если мы хотим описывать наши редьюсеры для todos
так:
1 2 3 4 5 6 |
|
То мы можем написать следующий хелпер для достижения этого:
1 2 3 4 5 6 7 8 9 |
|
Это было нетрудно, не правда ли? Redux не обеспечивает такие функции-хелперы по умолчанию, потому что существует слишком много способов написать их. Возможно, Вам захочется автоматически конвертировать простые JS-объекты в иммутабельные для «упразднения» состояния сервера. Возможно, Вы захотите объединять возвращаемое состояние с текущим. Может быть много подходов для “отлова всех” обработчиков. Все они зависят от соглашений, которые Вы выберите для Вашей команды и проекта.
API редьюсеров Redux заключается в том, что (state, action) => state
, но то, как Вы это реализуете, решать Вам.