У событий и действий есть отношение 1:1 в Redux?
У событий (событий DOM или системных событий) есть отношение 1:1 к действиям? то есть одно событие щелчка должно вызвать только одно действие?
Например, скажем, у нас есть страница, которая отображает таблицу из 10 строк и 2 столбцов. Каждая строка имеет поле Product и поле Amount. Поле Amount имеет диапазон ввода с диапазоном [0, 10]. Пользователь может установить количество каждого продукта индивидуально.
Пользователю также предоставляется 2 варианта, посредством использования 2 кнопок.
- Нажатие на вторую кнопку отключит все продукты, кроме первого, в таблице (фактически установив их количество на 0, и пользователь больше не сможет взаимодействовать с ними, чтобы установить их количество). Давайте назовем это
Option B
- Нажатие на первую кнопку включает все продукты после первого (по умолчанию для каждого из них устанавливается значение "1"), и пользователь может снова взаимодействовать с ними, чтобы установить их суммы индивидуально. Давайте назовем это
Option A
,
Вариант А выбран: | ПРОДУКТ | СУММА | | ------------------ | ----------- | | Продукт А | - 4 + | | Продукт Б | - 0 + | | Продукт C | - 4 + | ```````````````````````````````` ` _________ | Вариант А | ВАРИАНТ Б `` `` `` `` `` Вариант B выбран: | ПРОДУКТ | СУММА | | ------------------ | ----------- | | Продукт А | - 4 + | | Продукт Б | Отключено | (Сумма == 0) | Продукт C | Отключено | (Сумма == 0) ```````````````````````````````` ` _________ ВАРИАНТ А | ВАРИАНТ Б | `` `` `` `` `` Вариант А выбран снова: | ПРОДУКТ | СУММА | | ------------------ | ----------- | | Продукт А | - 4 + | | Продукт Б | - 1 + | | Продукт C | - 1 + | ```````````````````````````````` ` _________ | Вариант А | ВАРИАНТ Б `` `` `` `` ``
Состояние этого "приложения" описывается этим простым объектом
state = {
option : <String>,
products : [
{
name : <String>,
amount : <Integer>
}, ...
]
}
У нас также есть эти 4 простых создателя действий:
function setOption(option) {
return { type : 'SET_OPTION', option : option};
}
function incAmount(productName) {
return {
type : 'INCREMENT_AMOUNT',
product : productName
}
}
function decAmount(productName) {
return {
type : 'DECREMENT_AMOUNT',
product : productName
}
}
function setAmount(productName, amount) {
return {
type : 'SET_AMOUNT',
payload : { product : productName, amount : amount }
}
}
Для простоты у нас есть только один редуктор.
В этом примере, выбрав Option B
должен иметь следующие эффекты на состояние:
- + Изменить
option
вB
- Установите количество каждого
product
после первого0
выбирающий Option A
должны иметь следующие эффекты на состояние соответственно:
- + Изменить
option
вA
- Установите количество каждого
product
после первого1
Увеличение количества продукта А должно иметь следующие последствия для государства:
- Увеличить количество продукта А на 1
Каков был бы правильный способ осуществить эти изменения?
а) иметь onClick
обработчик option
кнопки делают следующее:
- Огонь
store.dispatch(setOption(option))
- Для каждого продукта после первого запуска
store.dispatch(setAmount(productName, amount))
(amount
= 1 для варианта A, 0 для варианта B)
б) иметь onClick
обработчик option
кнопки делают следующее:
Огонь
store.dispatch(setOption(option))
И изменить редуктор
option
так же хорошо какamount
каждого продукта после первого до указанной суммы (amount
= 1 для варианта A, 0 для варианта B)
Если мы пойдем с а) каждый случай в switch (action) {}
Заявление редуктора касается только одного аспекта состояния, но мы должны запустить более одного действия из одного click
событие
Если мы пойдем с б) мы запускаем только одно действие из click
событие, но случай для SET_OPTION
в редукторе не только меняет option
но также amount
продуктов.
2 ответа
На этот вопрос нет общего ответа, поэтому мы должны оценивать его в каждом конкретном случае.
При использовании Redux вы должны стремиться поддерживать баланс между простотой использования редукторов и сохранением значимости журнала действий. Лучше всего, когда вы можете прочитать журнал действий и имеет смысл, почему что-то произошло. Это аспект "предсказуемости", который приносит Redux.
Когда вы отправляете одно действие, и разные части состояния изменяются в ответ, легко понять, почему они изменяются позже. Если вы устраняете проблему, вы не перегружены количеством действий, и каждая мутация может быть отслежена тем, что сделал пользователь.
В отличие от этого, когда вы отправляете несколько действий в ответ на одно пользовательское взаимодействие, труднее сказать, почему они были отправлены. Они загромождают журнал действий, и если в том, как они были отправлены, есть ошибка, журнал не обнаружит причины, лежащие в основе.
Хорошее эмпирическое правило заключается в том, что вы никогда не хотите dispatch
в петле. Это крайне неэффективно и, как отмечалось выше, скрывает истинную природу того, почему произошли изменения. В вашем конкретном примере я бы порекомендовал запустить одно действие.
Однако это не означает, что запуск одного действия - это всегда путь. Как и все, это компромисс. Существуют допустимые случаи, когда более удобно запускать несколько действий в ответ на одно пользовательское взаимодействие.
Например, если ваше приложение позволяет пользователям отмечать продукты, удобнее разделять CREATE_TAG
а также ADD_TAG_TO_PRODUCT
действия, потому что в то время как в этом сценарии они происходят одновременно, они могут также происходить отдельно, и может быть проще написать редукторы, которые обрабатывают их как различные действия. Пока вы не злоупотребляете этим шаблоном и не делаете что-то подобное в цикле, все будет в порядке.
Храните журнал действий как можно ближе к истории пользовательских взаимодействий. Однако, если это усложняет реализацию редукторов, рассмотрите возможность разделения некоторых действий на несколько, если обновление пользовательского интерфейса можно рассматривать как две отдельные операции, которые просто оказываются вместе. Не впадайте ни в одну из крайностей. Предпочитают чистоту редуктора идеальному бревну, но также предпочитают не отправлять в цикле ясности редуктора.
Чтобы добавить к превосходному ответу Дэна, когда вы идете по пути b), вы все равно можете обрабатывать отдельные части состояния, как вы сказали способом a), разделяя корневой редуктор на более мелкие, как показано в Redux docs. Вы должны разделять обработку состояний, составляя редукторы, а не произвольно отправляя другие действия. Как сказал Дэн, это помогает действиям, выражающим причину.