Redux:重用reducer来更新多个状态属性

Posted

技术标签:

【中文标题】Redux:重用reducer来更新多个状态属性【英文标题】:Redux: Reuse a reducer to update multiple state properties 【发布时间】:2019-02-25 09:48:30 【问题描述】:

也许这只是我 Redux 知识中缺失的一条信息,但即使搜索了几个小时,我仍然不知道如何创建一个可重用的 reducer 来更新许多状态属性。

假设我创建了一个简单的组件MySwitch,它提供了一个用户界面来编辑布尔值。有一个相关的动作和一个reducer来更新状态。我了解如何使其适用于该州的特定财产。但是如何创建组件(和相关的减速器)以使用商店中的任何布尔值,而不为每个组件创建一个特殊的减速器?

假设,状态如下所示:


  items: 
    house:  isBig:true, location: ... ,
    car:  isMine:true, isBroken:false, ... 
  ,
  books: [
     id:1, available:true, title:... ,
     id:2, available:false, title:... , ...
  ]

创建MySwitch 的各种实例以查看和编辑任何给定值(例如items.house.isBigitems.car.isMinebooks[1].available)并使用相同的reducer 更新它们的正确机制是什么?

我可以使用 connect 将属性注入到组件中,例如:

ASwitch = connect(state => (
  valueToEdit: state.items.car.isMine
)(MySwitch)

但我不知道如何将它传递给减速器以及如何让它更新状态的相应部分。我想我可以提供行动的路径(即"items.car.isMine"):

export const editBoolean = ( path, value ) => 
  return 
    type: 'BOOL_EDIT',
    payload:  path, value 
  

然后在reducer中使用这样的东西:

case 'BOOL_EDIT': 
  const  path, value  = action.payload;
  return 
    ...state,
    [path]: value
  

但它不起作用,似乎 ES6 不支持可变计算属性,而是创建了像 state["items.car.isMine"] 这样的属性。

我不知道如何在这里继续。感谢您的帮助。

【问题讨论】:

您的想法是对的,但您需要通过您的路径遍历状态树,以便使用更新的节点创建 nextState 的浅表副本 好的,我可以做到(事实上,我已经准备好了一个遍历函数,因为我正在其他地方使用它)。我只是认为 Redux 中有更好的机制来做到这一点…… 【参考方案1】:

我认为您的想法是正确的,但您的问题并不完全针对 redux,您只需要使用您希望通过路径定位的更新节点来克隆您的状态。

你可以使用这样的东西

const singlePathReducer = (state,  payload:  path, value  ) => path.reduce(( nextState, branch = nextState ), node) => 
  if (node !== path[path.length - 1]) 
    // this was the part you were missing; you need to walk down your state tree
    // by passing a reference of the current branch to the next reduce callback
    return  nextState, branch: nextState[node] ;
  

  // mutate your nextState, this is ok
  branch[node] = value;
  return nextState;

  // don't mutate state, create a copy
,  nextState:  ...state  );

如果我没有弄错 js 中的引用传递是如何工作的,那么这应该可以工作。完整的实现是这样的(保持你的singlePathReducer fn 分开可以更容易进行单元测试):

const rootReducer = (state, action) => 
  switch action.type 
    case 'BOOL_EDIT':
      return singlePathReducer(state, action);
    default:
      return state;
  
;

【讨论】:

谢谢,我避免了这种方式,因为我认为在 Redux 中必须有一个工具来解决这个问题……好的,我会尝试使用路径遍历……谢谢! 查看immutable-js 库。你真的想抽象出这种更新状态的方式,而不是用这种方式处理状态。 如果 OP 真的想要计算属性 redux 是你应该使用的最后一件事...... mobx 或更合适的东西【参考方案2】:

*免责声明:这对我来说有点像 Redux 反模式,因为它允许使用单个操作将存储中的任何值设置为任何值。明智地使用它。

````
const set = require("lodash/set");
const  produce  = require("immer");

case 'BOOL_EDIT': 
  const  path, value  = action.payload;
  return produce(state, draft => 
    set(draft, path, value);
  );

```

editBoolean("items.car.isMine", false)

【讨论】:

谢谢,我不熟悉produceset 方法,我必须先检查它们是如何工作的。无论如何,我试图偏爱最佳实践并避免反模式,所以我可能不应该使用它。 ;) 'k,很酷,但是我提到的反模式是您提出的具有可以修改您状态中的任何属性的动作和减速器的模式,而不是这个特定的实现。您确实需要一个单独的操作来修改您的状态中的每个属性,以便您的类型更像“ITEMS_CAR_ISMINE”。 我明白这一点。我将重新考虑只创建哑元这样的组件,并在其容器组件上保持所有商店的更新。我将不得不更改应用程序逻辑,但最终可能会更好。谢谢。

以上是关于Redux:重用reducer来更新多个状态属性的主要内容,如果未能解决你的问题,请参考以下文章

Redux (with React) Reducer switch 语句不更新状态

如何重用 Redux Toolkit createSlice 函数中的 reducer 逻辑?

更新:没有调用 Redux reducer 来更改 CSS 类

React Redux reducer 未更新状态

Redux 开发工具状态图显示reducer 正在更新另一个reducer

Reducer 状态没有被新对象更新 [redux, redux-toolkit, normalize]