【问题标题】:Do events and actions have a 1:1 relationship in Redux?Redux 中的事件和动作是 1:1 的关系吗?
【发布时间】:2016-05-26 04:56:54
【问题描述】:

事件(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 + | ```````````````````````````````` _________ |选项 A|选项 B ````````` 选择了选项 B: |产品 |数量 | |-------|-----------| |产品A | - 4 + | |产品B |残疾人 | (金额 == 0) |产品C |残疾人 | (金额 == 0) ```````````````````````````````` _________ 选项 A |选项 B| ````````` 再次选择选项A: |产品 |数量 | |-------|-----------| |产品A | - 4 + | |产品B | - 1 + | |产品C | - 1 + | ```````````````````````````````` _________ |选项 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 应该会对状态产生以下影响:

  • 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))amount = 选项 A 为 1,选项 B 为 0)

b)option 按钮的onClick 处理程序执行以下操作:

  • 触发store.dispatch(setOption(option))

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

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

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

【问题讨论】:

    标签: javascript events action redux


    【解决方案1】:

    为了补充 Dan 的出色答案,当您采用 b) 方式时,您仍然可以像 a) 方式中所说的那样处理状态的不同部分将根减速器拆分成较小的减速器,例如Redux docs show。您应该通过组合 reducer 来拆分状态处理,而不是通过任意调度其他操作。正如 Dan 所说,它有助于表达为什么的行动。

    【讨论】:

    • 感谢您的意见。这正是我最后所做的,顺便说一句,我真的很享受这个过程;这就像一个迷你脑筋急转弯
    【解决方案2】:

    这个问题没有一般的答案,所以我们必须逐案评估。

    使用 Redux 时,您应该努力在保持 reducer 简单和保持操作日志有意义之间取得平衡。当您可以阅读操作日志并且了解事情发生的原因时,这是最好的选择。这就是 Redux 带来的“可预测性”方面。

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

    相比之下,当您调度多个操作以响应单个用户交互时,更难说明为什么它们被调度。它们使操作日志变得杂乱无章,如果它们的发送方式有误,日志不会发现根本原因。

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

    但这并不意味着触发单个动作始终是可行的方法。像所有事情一样,这是一个权衡。在某些情况下,触发多个操作以响应单个用户交互会更方便。

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

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

    【讨论】:

    • 根据我的经验,异步操作也会导致每个用户操作有多个 redux-actions。例如,如果用户单击“保存”按钮,您可能首先发送同步操作以禁用用户界面的某些部分(如“保存”按钮),然后发送异步操作以实际远程保存对象。
    • 或者跟进这个例子,如果你有一个创建按钮负责创建资源并将其分配给用户,如果你试图保持安静,它应该会触发 2 个 API 请求。你会怎么做呢?
    • 我认为 RESTful 是一个坏主意,如果这意味着你的请求效率低下。我当然会把它作为一个单一的请求。在这两种情况下,没有什么能阻止您调度多个操作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多