哪个应该更简单,行动还是减少?

时间:2016-12-01 09:05:14

标签: reactjs redux immutability redux-thunk

这是使用thunk时编写动作的一种方法,这会导致减速器非常简单。

   getCurrentUserPicture(){
        return (dispatch,getState) => {
            dispatch({type: "isloading", isLoading: true}); // shows a loading dialog
            dispatch({type: "errorMessage"}); // clear errorMessage
            dispatch({type: "warningMessage"}); // clear warningMessage

            const userId = getState().user.get("currentUser").id;
            getUserPicture(userId) // get from backend
                .then(picture => {
                    dispatch({type: "userPicture", picture});
                    dispatch({type: "isLoading", isLoading: false}); 
                }
            )
            .catch(e=>{
                dispatch({type: "errorMessage", e});
                dispatch({type: "isLoading", isLoading: true});
                }
            )
        }
    }

使用减速器包括:

export reducer(state = initialState, action = {}) {
    switch(action.type) {
        case "isLoading":
            return state.set("isLoading", action.isLoading)

这是另一种方法,其中行动更清洁"但减速器更复杂:

   getCurrentUserPicture(){
        return (dispatch,getState) => {
            dispatch({type: "gettingCurrentUserPicture", true});

            const userId = getState().user.get("currentUser").id;
            getUserPicture(userId)
                .then(picture => {
                    dispatch({type: "retrievedCurrentUserPicture", picture}); 
                }
            )
            .catch(e=>{
                dispatch({type: "errorRetrievedCurrentUserPicture", e});
                }
            )
        }
    }

在上述操作的reducer中,例如:

export reducer(state = initialState, action = {}) {
    switch(action.type) {
        case "gettingCurrentUserPicture":
            return state.set("isLoading", true)
                        .delete("errorMessage")
                        .delete("warningMessage")

一种方法比另一方更好吗?

4 个答案:

答案 0 :(得分:7)

为什么不两者?

操作 Reducers 应尽可能简单。

这说起来容易做起来难,但通过引入其他概念和模式,例如选择器 sagas ,甚至是简单的实用程序函数/类 ,动作和减速器的复杂性可以大大降低。

操作

我知道术语"广播"在磁通世界中真的不赞成 - 但我发现它可以帮助我改进我行动中应该属于的东西。该行动应该"广播"向全世界发生了刚刚发生的事情 - 它并没有个人关心接下来的事情,或者有多少减速机选择回应它 - 它只是信使。即:它不关心应用程序在上述行动后的样子。

我的观点是业务逻辑/规则要么直接属于这里,要么可以通过帮助实用程序功能在这里引导。请参阅以下有关异步和实用程序的部分。

  

它应描述发生的事情。 描述,描述,描述

减速

Reducers应该以尽可能少的数据形成应用程序的完整(ish)表示。在可能的情况下,保持状态树的标准化,以确保您保持最小状态。

我的意见是,这些应该尽可能轻,最多应该只是基本的对象操作 - 添加/删除键或更新值。

  

根据我刚刚收到的描述,国家应该是什么样的? (来自行动)

方法

这听起来很疯狂,但是rubber duck debugging(在这种情况下,橡皮鸭编程)确实有助于规划你的redux结构。

我会通过以下步骤说话(有时甚至是大声说话),例如:

  
      
  • 将鼠标悬停在帖子标题上并点击修改
  •   
  • 该应用交换到"编辑帖子"页
  •   
  • 您可以编辑标题字段(字段将更新)
  •   
  • 您可以编辑正文字段(字段将更新)
  •   
  • 应用程序将每90秒保存为草稿,在自动保存期间右上角会出现一个小保存图标,但您可以继续工作
  •   
  • 您也可以随时点击保存按钮进行保存。在此期间,您无法进行编辑,并且您将被重定向到帖子索引页面
  •   

通过查看描述的内容以及结果是状态的变化,可以将其松散地转化为行动和减少者:

  • 点击修改:操作 - 我们描述到应用时已点击了ID X的帖子
  • 交换到"编辑帖子" page:Reducer - 当我们"听到" a"发布编辑"更改应用程序的状态,使其现在显示帖子编辑页面
  • 编辑标题/正文:操作 - 我们描述哪个字段以及用户输入的值。
  • 更新标题/正文:Reducer - 根据输入的字段
  • 更改应用的状态
  • 自动保存:动作/减速器/动作/减速器
    • 操作:我们描述自动保存已开始的应用
    • 减速器:应用程序状态更改以显示正在进行的自动保存
    • 行动:我们描述自动保存已完成
    • 缩减器:应用程序更改状态以隐藏正在进行的自动保存

我想要做的一点,虽然长时间的风,但它不是应该更简单,它的东西(种类)属于哪里,但很可能你会看到你的行动结束与减速器相比更复杂。

但是,我的行为仍然不够......

上述所有内容都很容易说,但是如何保持这些业务的清洁/苗条/轻/简单/等等?

在一天结束时,大多数业务逻辑与React / Redux没有多大关系 - 因此通常可以将它们推到实用功能或类中。这有几个原因很好a)更容易测试,因为它没有与React / Redux的零链接和b)保持你的行动轻,c)更多的封装 - 对react / redux的了解最少也不错的事情。

一天结束时只是Javascript - 导入自定义业务类或实用程序功能没有任何问题。

但是,async ..

异步通常会很快开始混乱。如果你想要清理你的行为,Saga的确非常值得一看,但是如果你的团队不在发电机之间,那么确实会引入一定的学习曲线。在这种情况下,thunk仍然有用,并且记住你可以将多个异步函数捆绑到thunk可以播放的单个promise中(同样,这个分组的promise可以分成实用函数类 - 如{{1}这可能包含4或5个异步提取,并返回单个promise)。

旁注 - 您应该像在第一个示例中那样,从单个流中多次调度多次。如果可能,尝试创建将整个操作分组为一个的父操作。因此,使用您的第一个示例,它可能是:

generateSaveSequencePromise()

然后你的各种减速器应该清除他们的消息队列,或者如果他们"听到"那种类型来了。

答案 1 :(得分:4)

如果你采用任何一种方式都有优点/缺点。但我的选择是让 reducer比动作/动作创建者更简单

在Reducers中处理业务逻辑(保持操作更简单)

在您的Reducer中执行所有同步业务逻辑,使操作/操作创建者更加简单。

优点

  1. 根据您的业务逻辑,您可以决定下一个应用的状态。 应该。
  2. 在Reducer中处理业务逻辑很容易。
  3. 缺点

    1. 您只能执行同步任务(但有中间件 它支持异步任务)。
    2. 无法访问dispatch
    3. 如果拆分缩减器,则无法访问整个应用程序状态。
    4. 在Action Creators中处理业务逻辑(使Reducer更简单)

      您可以随时执行一些业务逻辑并触发操作。

      优点

      1. 您可以访问dispatch
      2. 您可以执行异步任务(请参阅 redux-thunk)。
      3. 即使您已合并,行动创作者也可以访问整个州(redux-thunk) 减速器。
      4. 缺点

        1. 没有简单的方法可以使业务逻辑基于所有的工作 动作。例如,如果要为所有操作附加ID, 然后你必须附加一个附有所有动作的功能
        2. Here会对此进行详细讨论。

答案 2 :(得分:2)

如果你的actionCreators很复杂,召唤大量的小动作,你最终会慢慢走向你的状态,有效地拥有了“安装者”。 (每个动作只是特定领域的设定者)。如果actionCreator具有广泛的影响,你最终必须派遣“setter”。与各种不同的子减速器相关的动作 - 您将这些不同减速器的领域知识推向动作创建者。当您更改子减速器时,您必须找到所有分配相关操作并适当调整它们的动作创建者。

只有表明发生了什么的胖动作(例如已经发出请求,成功响应已经回来,计时器被触发,点击了一个按钮),您可以管理对业务逻辑的更改而无需更改actionCreators。例如,您决定在操作期间开始显示加载掩码 - 您现在不需要更新所有操作创建者以触发loadingStart和loadingEnd操作,您只需更新缩减器以适当地设置加载状态。

就我个人而言,我认为复杂应用程序的最佳模型是使actionCreators变得微不足道(或完全删除它们,只是直接从连接的组件发送动作有效负载),而是使用redux-saga(https://github.com/yelouafi/redux-saga)来处理你的异步和不纯净的东西 - 它是一个更强大的模型。例如,它可以很容易地执行去抖动和限制操作,对商店状态的变化以及操作等做出反应。

答案 3 :(得分:2)

我同意@TomW的意见,即派出许多小动作会导致行动成为制定者。

动作创建者最初只是非常简单的函数,它们构成了非常简单的对象,然后被分派。

调度操作意味着将其发送到要处理的管道。执行动作应该是减速器的责任。行动本身不应该执行。相反,它应该是需要做的事情的简单描述。

异步操作创建者是事后的想法,需要安装售后零件才能工作。它应该是异步的,并且尽可能地遵循动作应该在reducers 中执行的想法,即动作对象是动作的描述。

您可以给某人反汇编并将其称为源代码。当然,这是源代码。但它的水平远低于真实来源,并且包含的​​信息要少得多。原始陈述是高级别的,因此包括有关意图的信息,而不仅仅是有关如何到达目的地的信息。例如,变量名称不存在于反汇编中。

以同样的方式,调度许多本身并不表达意图的行为,不适合Redux。

事实上,该操作应始终描述意图,并尽可能多地省略实施细节。

Redux并不只是应用操作。中间件可以记录它们并将它们还给它们。您可以查看历史记录,看看发生了什么。

如果操作不包括意图或它们存在的原因,那么历史记录只是一组有序的更改,并且可以通过比较两个连续状态并检查哪些属性发生更改来导出操作类型。这只是表明动作类型最终没有任何意义。该动作类型应该具有大部分含义。

异步行动创作者有点像kludge。被派遣的东西并不是像Redux那样的强项,而是一种功能。如果您使用Redux Dev工具,则不会记录正在发送的事实。它无法重播。因此,异步操作创建者不会创建操作。

此外,找到无效状态应该允许您找到将该状态置于其中的操作,并从那里找到处理该操作的代码。这是reducer代码。但是,如果真正的问题不是在减速器中,而是在发送动作的动作创建者,那么它将更难找到,因为它不清楚哪个派遣动作。

(Ab)使用异步操作来实现业务逻辑意味着放弃使用Redux的一些最重要的好处。