redux mapStateToProps的最佳实践?

时间:2016-10-27 02:52:00

标签: redux react-redux

据我所知,reducers更改树的状态,mapStateToProps转换UI的状态树。但是,边界并不清楚。例如,考虑来自"计算派生数据" docs(http://redux.js.org/docs/recipes/ComputingDerivedData.html)。见下文。

我的第一直觉是将可见待办事项的计算放在reducer (而不是mapStateToProps)中,也就是说,每当todo或可见性过滤器发生变化时,都会更新列表已完成或有效的待办事项。这有几个好处:

  1. 无需Reselect
  2. 在一个地方拥有所有逻辑有助于减少学习曲线(在入职时)并且可能也更容易测试(因为mapStateToProps的集成测试更简单,如果不存在的话)。
  3. 另一方面,2)是主观的。因此,对mapStateToProps的指导会有所帮助。

    const getVisibleTodos = (todos, filter) => {
      switch (filter) {
        case 'SHOW_ALL':
          return todos
        case 'SHOW_COMPLETED':
          return todos.filter(t => t.completed)
        case 'SHOW_ACTIVE':
          return todos.filter(t => !t.completed)
      }  
    }
    
    const mapStateToProps = (state) => ({
      todos: getVisibleTodos(state.todos, state.visibilityFilter)
    })
    
    const mapDispatchToProps = (dispatch) => ({
      onTodoClick: (id) => dispatch(toggleTodo(id))         
    })
    
    const VisibleTodoList = connect(
      mapStateToProps,
      mapDispatchToProps
    )(TodoList)
    

    响应@DDS更新:

      

    根据一个动作更新多个相互关联的状态意味着这些状态可能会变得不同步......这可能意味着visibleTodoList获取了原始文件中不存在的项目。

    如果由多个相互关联的状态表示visibilityFiltertodos,那么根据redux文档,一个惯用的解决方案是重构状态树,使它们成为一个状态。文档中还提到了其他方法。当然,正如您所说,您现在有责任确保始终调用计算派生状态(可见待机)的代码。一旦代码库变大,在返回状态之前执行其他转换的自定义combineReducer(另一个惯用解决方案)是有意义的。

      

    请注意,代码位于单独的文件中,并且无法保证reducers的执行顺序。此外,减速器通常无法访问兄弟状态,这意味着他们无法从中获取数据

    请参阅上面的评论。

      

    上面的例子可能是设计的,但关键是要使数据稳定,最好是每个组件都可以依赖单个源,这需要对数据进行规范化。

    是的,这一切都归结为归一化与非规范化状态。我还不确定规范化状态总是总是的方式......出于同样的原因,NoSQL数据库有时候要走的路。

      

    关于更复杂状态[归一化和非规范化状态]的推理变得非常困难。这就是为什么最好不要将派生数据置于状态。

    啊,我明白你的意思了。从现在开始的六个月,我可能看不到visibleTodos是派生状态(因此应该被视为只读)。会产生意想不到的结果。

2 个答案:

答案 0 :(得分:3)

注意:根据我的个人经验,这是我的两分钱,并不一定符合最佳实践。

Redux状态

这应该被规范化,主要是因为它使得写入(插入/更新/删除)易于推理。 规范化redux状态意味着您不应该将派生数据存储在redux状态。

派生数据:

我使用react / redux的个人经验(在出现http://redux.js.org/docs/recipes/ComputingDerivedData.html上的好文档之前)使我尝试遵循您(OP)正在努力的事情:简化代码编写的地方。 在接受归一化原则之后,我开始编写派生或“状态视图”逻辑的自然场所在于反应组件的渲染功能(现在听起来有点难看)。随着代码的发展,渲染函数通过创建派生函数并将它们保持在反应组件之外变得更加整洁。

这为使用代码库的人创建了一个简单的心智模型:

  • redux-state:规范化商店
  • mapStateToProps:只是以愚蠢的方式将状态映射到道具值
  • ReactComponent:包含“查看”状态的正确部分然后呈现它的所有逻辑。进一步模块化在作者认为必要时完成。

答案 1 :(得分:2)

使用Reselect的原因是将它放在reducer中并处于状态类似于使用React而不是jQuery的原因。

基于一个动作更新多个相互关联的状态意味着这些状态可能变得不同步。也许一个reducer会将ADD_ITEM解释为" upsert"当另一个人在几个月之后由另一个人编写在另一个文件中时,将其视为"插入允许重复的文件"。这可能意味着visibleTodoList获取了原始文件中不存在的项目。

请注意,代码位于单独的文件中,并且无法保证reducers的执行顺序。此外,减速器通常无法访问兄弟状态,这意味着它们无法从中获取数据。

上面的例子可能是设计的,但关键是要使数据稳定,最好是每个组件都可以依赖单个源,这需要对数据进行规范化。存储派生数据意味着将相同数据存储在多个位置和形式中,但是相互依赖。

拥有单一来源并且单向数据流,可防止对哪些数据具有权威性存在分歧。

国家应该被视为洁净室数据。它具有许多可靠且易于推理的属性:

  • 所有状态都是不可变的,这意味着它可以存储在任何地方并被视为值,而不用担心以后会被外部代码修改。
  • 所有状态都是可序列化的,可以保证它不包含代码或循环,并且可以轻松发货和存储。
  • 它被标准化,因此它只包含每个数据的一个副本。国家的不同部分之间不存在不同步或分歧。这使得国家更难以使内部不一致。

关于更复杂状态的推理变得非常困难。这就是为什么最好不要将派生数据置于状态。

请注意,在编写代码时看起来像一个伟大而简单的想法可能会让你在多年后绊倒你,当时这个小小的项目已经发展成为备受追捧的必备工具。在第一轮中修改工作代码并不难,所以做Redux Way是一个非常具有前瞻性的策略。