在 Redux 中事件和动作是 1:1 的关系吗?
问题:在 Redux 中事件和动作是 1:1 的关系吗?
事件(DOM 事件或系统事件)是否与动作有 1:1 的关系?即一次单击事件是否应该只触发一个操作?
例如,假设我们有一个显示 10 行 2 列的表格的页面。每行都有一个 Product 字段和一个 Amount 字段。 Amount 字段有一个范围输入,范围为 [0, 10]。用户可以单独设置每个产品的数量。
通过使用 2 个按钮,还为用户提供了 2 个选项。
-
按下第二个按钮将禁用表中除第一个产品之外的所有产品(有效地将其数量设置为 0,并且用户无法再与它们交互以设置其数量)。我们称它为
Option B -
按下第一个按钮会启用第一个之后的所有产品(默认情况下,将每个产品的数量设置为 1),用户可以再次与它们交互,单独设置它们的数量。我们称它为
Option A。
选择了选项 A:
|产品 |数量 |
|-------|-----------|
|产品A | - 4 + |
|产品B | - 0 + |
|产品C | - 4 + |
\ ZWZ100009 \ ZWZ100010 \ ZWZ100011 \ ZWZ100012 \ ZWZ100013 \ ZWZ100014 \ ZWZ100015 \ ZWZ100016 \ ZWZ10001000100017100017 0001000100010001000100010001000101010 \ ZWZ110 \ zwz110 \ ZZZZ11 QZZZ1 QZ1 QZ1 QZ1 QZ1×ZZ100×
_________
| 选项A|选项 B
`````````
选择了选项 B:
|产品 |数量 |
|-------|-----------|
|产品A | - 4 + |
|产品B |残疾人 | (金额 u003du003d 0)
|产品C |残疾人 | (金额 u003du003d 0)
\ ZWZ100029 \ ZWZ100030 \ ZWZ100031 \ ZWZ100032 \ ZWZ100033 \ ZWZ100034 \ ZWZ100035 \ ZWZ100036 \ ZWZ100036 \ ZWZ100037
_________
选项 A |选项 B|
`````````
再次选择选项A:
|产品 |数量 |
|-------|-----------|
|产品A | - 4 + |
|产品B | - 1 + |
|产品C | - 1 + |
\ ZWZ100049 \ ZWZ100050 \ ZWZ100051 \ ZWZ100052 \ ZWZ100053 \ ZWZ100054 \ ZWZ100055 \ ZWZ100056 \ ZWZ100056 \ ZWZ100057 \ ZWZ100058 \ ZWZ10001000161616161611011016零\ ZWZ100 \ ZWZ100 \ ZWZ100 \ ZWZ100×
_________
| 选项A|选项 B
`````````
这个“应用程序”的状态由这个简单的对象描述
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 }
}
}
为了简单起见,我们只有一个 reducer。
在这个例子中,选择Option B应该会对 state 产生以下影响:
-
将
option更改为B -
设置第一个之后的每一个
product的数量为0
选择Option A应分别对状态产生以下影响:
-
将
option更改为A -
设置第一个之后的每一个
product的数量为1
增加产品 A 的数量应该对状态产生以下影响:
- 产品A的数量加1
实现这些更改的_正确_方式是什么?
a) 让option按钮的onClick处理程序执行以下操作:
-
开火
store.dispatch(setOption(option)) -
对于第一个触发后的每个产品
store.dispatch(setAmount(productName, amount))(amountu003d 1 用于选项 A,0 用于选项 B)
b) 让option按钮的onClick处理程序执行以下操作:
- 开火
store.dispatch(setOption(option))
并让减速机将第一个产品之后的每个产品的option和amount更改为指定的数量(选项 A 为amountu003d 1,选项 B 为 0)
如果我们使用 a) 在 reducer 的switch (action) {}语句中的每个案例只处理状态的一个方面,但是我们必须从一个click事件中触发多个动作
如果我们使用 b) 我们只会从click事件中触发一个动作,但是减速器中SET_OPTION的情况不仅会更改option,还会更改产品的amount。
解答
这个问题没有一般的答案,所以我们必须根据具体情况进行评估。
使用 Redux 时,您应该努力在保持 reducer 简单和保持操作日志有意义之间保持平衡。最好是您可以阅读操作日志并且_为什么_事情发生是有道理的。这就是 Redux 带来的“可预测性”方面。
当您调度单个动作,并且状态的不同部分作为响应发生变化时,很容易告诉_为什么_它们稍后会发生变化。如果您调试问题,您不会被大量的操作所淹没,并且每个突变都可以追溯到用户所做的事情。
相比之下,当您为响应单个用户交互而调度多个操作时,更难告诉_为什么_它们被调度了。它们使操作日志混乱,如果它们的发送方式有误,日志将无法揭示根本原因。
一个好的经验法则是,您永远不想在循环中使用dispatch。这是非常低效的,并且如上所述,掩盖了_为什么_发生变化的真实性质。在您的特定示例中,我建议触发单个操作。
然而,这并不意味着触发单个动作是_总是_要走的路。像所有事情一样,这是一个权衡。当响应单个用户交互时触发多个操作更方便时,存在有效的情况。
例如,如果您的应用程序允许用户标记产品,将CREATE_TAG和ADD_TAG_TO_PRODUCT操作分开会更方便,因为虽然在这种情况下它们同时发生,但它们也可能单独发生,并且编写处理的 reducer 会更容易它们作为不同的动作。只要您不滥用此模式并且不循环执行此类操作,就可以了。
尽可能将操作日志与用户交互的历史记录保持一致。但是,如果它使 reducer 难以实现,请考虑将一些操作分成几个,如果 UI 更新可以被认为是两个恰好在一起的单独操作。不要落入任何一个极端。更喜欢 reducer 清晰而不是完美的日志,但也不喜欢循环调度而不是 reducer 清晰。
更多推荐
所有评论(0)