问题:在 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))

并让减速机将第一个产品之后的每个产品的optionamount更改为指定的数量(选项 A 为amountu003d 1,选项 B 为 0)

如果我们使用 a) 在 reducer 的switch (action) {}语句中的每个案例只处理状态的一个方面,但是我们必须从一个click事件中触发多个动作

如果我们使用 b) 我们只会从click事件中触发一个动作,但是减速器中SET_OPTION的情况不仅会更改option,还会更改产品的amount

解答

这个问题没有一般的答案,所以我们必须根据具体情况进行评估。

使用 Redux 时,您应该努力在保持 reducer 简单和保持操作日志有意义之间保持平衡。最好是您可以阅读操作日志并且_为什么_事情发生是有道理的。这就是 Redux 带来的“可预测性”方面。

当您调度单个动作,并且状态的不同部分作为响应发生变化时,很容易告诉_为什么_它们稍后会发生变化。如果您调试问题,您不会被大量的操作所淹没,并且每个突变都可以追溯到用户所做的事情。

相比之下,当您为响应单个用户交互而调度多个操作时,更难告诉_为什么_它们被调度了。它们使操作日志混乱,如果它们的发送方式有误,日志将无法揭示根本原因。

一个好的经验法则是,您永远不想在循环中使用dispatch。这是非常低效的,并且如上所述,掩盖了_为什么_发生变化的真实性质。在您的特定示例中,我建议触发单个操作。

然而,这并不意味着触发单个动作是_总是_要走的路。像所有事情一样,这是一个权衡。当响应单个用户交互时触发多个操作更方便时,存在有效的情况。

例如,如果您的应用程序允许用户标记产品,将CREATE_TAGADD_TAG_TO_PRODUCT操作分开会更方便,因为虽然在这种情况下它们同时发生,但它们也可能单独发生,并且编写处理的 reducer 会更容易它们作为不同的动作。只要您不滥用此模式并且不循环执行此类操作,就可以了。

尽可能将操作日志与用户交互的历史记录保持一致。但是,如果它使 reducer 难以实现,请考虑将一些操作分成几个,如果 UI 更新可以被认为是两个恰好在一起的单独操作。不要落入任何一个极端。更喜欢 reducer 清晰而不是完美的日志,但也不喜欢循环调度而不是 reducer 清晰。

Logo

React社区为您提供最前沿的新闻资讯和知识内容

更多推荐