【问题标题】:Complex redux-loop examples?复杂的 redux 循环示例?
【发布时间】:2017-02-21 00:23:57
【问题描述】:

有没有使用redux-loop 处理复杂的ajax 工作流的例子?官方回购非常简约。从我能找到的一个例子(https://hackernoon.com/why-i-wrote-a-redux-async-outerware-277d450dba74#.2ocaloc58)来看,redux-loop 似乎与 redux-thunk 非常相似。

以下是一些复杂的 ajax 工作流程示例:

  • Ajax 工作流1。用户在结果表上选择两个不同的过滤器。每个过滤器启动一个 ajax 请求,然后无序解析。结果表应显示正确的过滤器选择。错误不应更新结果表。
  • Ajax 工作流 2
    • 用户开始生成报告(这是一个长期运行的过程)。
    • 用户切换到另一个报告。它应该取消或忽略挂起的“等待报告”操作。
  • 更复杂的工作流程(基于一个旧的 redux-saga 示例)
    • 用户按下登录按钮,将启动 ajax 请求以获取身份验证令牌
    • 要么
      • 用户立即按下注销按钮,这应该取消/忽略挂起的身份验证操作
      • 或者它应该在解析时存储身份验证令牌
    • 注销后或出现登录错误时应清除身份验证令牌

【问题讨论】:

    标签: ajax asynchronous redux redux-loop


    【解决方案1】:

    我将介绍第二个工作流程(登录)。

    在进入代码之前,值得注意的是redux-loopredux-saga 在异步控制流方面要简单得多。但本着Elm 的精神,重点是数据流——毫不奇怪,通常是通过数据类型来实现的。因此,从静态类型语言的角度思考是有帮助的。在HaskellElm 中,按数据类型对问题进行建模可能是有益的,它本身就编码了一个状态机:

    data LoginStatus data err =
        LoggedOut       | 
      , LoggedIn data   |
      , LoginError err  | 
      , Pending
    

    其中dataerr 是类型变量,表示登录数据类型(令牌)和登录错误。 JavaScript 是动态类型的,没有表达相同想法的好处 - 但是有很多动态技巧可以用来模仿标记的联合类型,例如LoginStatus。事不宜迟,代码如下:

    import {match} from "single-key";
    
    export default function reducer(state, action) {
      return match(state, {
        LoggedOut : () => loggedOutReducer(state, action),
        LoggedIn  : () => loggedInReducer(state, action),
        Pending : () => pendingReducer(state, action),
        LoginError : () => loginErrorReducer(state, action)
      });
    } 
    

    在这里,我将使用一个简单且鲜为人知的库singe-key 来实现非常基本的运行时联合类型。顾名思义,“单键”对象是一个只有一个键和一个值的对象,例如{ a: 1 }(“a”是键,1 是值)。我们将使用单键对象对状态进行建模——不同的键代表LoginStatus 的不同变体。几个例子说明:

    {
      LoggedOut : true
    }
    
    
    {
      LoggedIn : {
        token : 1235,
        user : { firstName: "John" }
      }
    }
    
    {
      Pending : true
    }
    

    了解清楚后,以下是主减速器中使用的子减速器:

    // state :: { LoggedIn: {/* some data * } }
    function loggedInReducer(state, action) {
      if (action.type === LOGOUT) {
        return {
          LoggedOut : true
        };
      }
      return state;
    }
    // state :: { Pending : true }
    function pendingReducer(state, action) {
      if (action.type === LOGIN_SUCCESS) {
        return {
          LoggedIn : {
            token : action.payload.token,
            user  : action.payload.user
          }
        };
      }
      if (action.type === LOGIN_ERROR) {
        return {
          LoginError : action.payload;  
        };
      }
      if (action.type === LOGOUT) {
        return {
          LoggedOut : true  
        };
      }
      return state;
    }
    // state :: { LoggedOut : true }
    function loggedOutReducer(state, action) {
      if (action.type === LOGIN) {
        return loop({ Pending: true }, Effects.promise(loginRequest));  
      }
      return state;
    }
    // state :: { LoginError : error }
    function loginErrorReducer(state, action) {
      if (action.type === LOGIN) {
        return loop({ Pending: true }, Effects.promise(loginRequest));  
      }
      return { LoggedOut : true }; 
    }
    

    这些类似于有限状态机中的转换,除了有时将数据附加到状态。每个单独的 reducer 都相当简单,只处理很少的动作类型。只有两个减速器返回效果:

    return loop({ Pending: true }, Effects.promise(loginRequest));
    

    这会将状态从 LoggedOut/LoginError 转换为 Pending 并指定一些副作用 - 将由 redux-loop 安排。您甚至可以将这两个变体合并为一个:{ LoggedOut : error | null },但我觉得拥有一个单独的 LoginError 状态从长远来看是有益的。

    有了一些数据类型的概念,这个问题可能比最初看起来更容易推理;你可以使用结构大致相同的 reducer 做同样的事情,只使用redux-thunk

    【讨论】:

    • 我很欣赏你的回应,但是......这种方法的问题在于它混合了责任。异步工作流+“正常”状态。这导致复杂的减速器。在实际项目中,这意味着您只是让创建脆弱代码变得更加容易。而且,您还没有解决如何实际执行上述工作流程。
    • 感谢您的反馈。这是合理的批评。对于最后一点,我相信我主要讨论了它是如何工作的。好吧,因为我今天手头有一些空闲时间,所以只是使用这里解释的减速器布局做了一个工作示例。它同时具有 redux-loopredux-sage 实现,具有几乎相同的减速器:https://github.com/yiransheng/redux-login-examples
    • 显式枚举类型的想法很好。不幸的是,正如他们在美国所说的那样,“你正在打败一匹死马。”此外,对于关注点的混合,您没有专用的 API 来处理异步性。因此,在现实世界的项目中,代码变得非常非常复杂(并且重复)。例如,您不处理“取消”案例。其次,FRP/sagas 在测试中获胜。你不需要模拟。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多