Redux:如何根据其他状态创建状态?

Posted

技术标签:

【中文标题】Redux:如何根据其他状态创建状态?【英文标题】:Redux: How to create state based on other state? 【发布时间】:2018-08-23 15:00:41 【问题描述】:

我正在尝试学习 Redux,所以:我正在学习 Action creators、actions、reducers、dispatchers...

现在,我正在尝试学习如何根据其他状态生成 x 状态。对于案子,姓名和爱好。

我有类型,常量:NAMESHOBBIES,它们被我的动作创建者使用,它们将它们作为类型和有效负载返回,例如: type: HOBBIES, request: payload

然后我有我的 reducer 文件,我在其中通过两种情况(和默认情况)的操作进行切换

我从我的主文件 App.js 进行调度,并以道具的形式访问这些状态。

function mapStateToProps(state) 
  return 
    names: state.names,
    hobbies: state.hobbies
  


function mapDispatchToProps(dispatch) 
  return bindActionCreators( grabNames, grabHobbies , dispatch);


export default connect(mapStateToProps,mapDispatchToProps)(myApp);

我正在使用 combineReducers (index.js)

const rootReducer = combineReducers(
  names: namereducer,
  hobbies: hobbyreducer
);

目前我有以下redux状态。

names: [
  name: "A", id: "1",
  name: "B", id: "2",
  name: "C", id: "3"
]

hobbies: [
  basedId: "1", hobby: "cooking",
  basedId: "2" hobby: "reading",
  basedId: "3" hobby: "gaming",
basedId: "1" hobby: "reading"
]

结果应该是包含每个basedId 的爱好的对象数组,当然遵循 Redux 的良好实践。

result: [
      name: "A", id: "1", hobby: ["cooking", "reading"],
      name: "B", id: "2", hobby: "reading",
      name: "C", id: "3", hobby: "gaming"
]

问题是我从未超越简单的地图和 [...state, action.data];

我试图找到解决方案,但我找不到。我非常感谢任何帮助,因为我是新手。

【问题讨论】:

只要“爱好”只包含一个字符串,考虑通过简单地将它们保存在名称缩减器内的数组中来维持这种 1:N 关系(一个人有很多爱好)。只有当“爱好”成为具有其他属性的更复杂对象时(显然,您不想在state.names 中复制),您才需要使用像reselect 这样的库来建立此映射。顺便说一句,mapStateToProps 也可以正常工作,这只是一种优化。 【参考方案1】:

我认为不要从您已存储的数据中生成新状态,因为您将存储相同(但已修改)的数据两次。而是在需要时执行此数据操作并构造您的 result 对象。

话虽如此,下面是你的答案,带有一点 ES6 香料:

names.map(name => (
    ...name, hobby: hobbies.filter(hobby => hobby.basedId === name.id).map(hobby => hobby.hobby)  
))

【讨论】:

【参考方案2】:

redux 原则之一是“单一事实来源”。 这意味着不要重复数据,它更高效,并且可以防止许多错误和问题。

但如果你只是想在两个 reducer 之间共享数据,你可以这样做 通过使用redux thunk middleware

基本上,您可以在使用 dispatch 启动操作之前从其他 reducer 状态读取数据。

简单的操作

function myAction() 
  return 
    type: DO_SOMTHING
  ;

thunk 操作

function myAction() 
  return (dispatch, getState) => 

    // get state contains all existing reducers states
    const  reducerTwo  = getState(); 

    dispatch(
      type: DO_SOMTHING,
      data: reducerTwo.prop
     );
  ;

【讨论】:

【参考方案3】:

这是reselect 的完美用例。您的代码基本上如下所示:

import  createSelector  from 'reselect';

const namesSelector = state => state.names;
const hobbiesSelector = state => state.hobbies;
const combineNamesAndHobbies = (names, hobbies) =>
  names.map(name => (
    ...name,
    hobby: hobbies.filter(hobby => hobby.basedId === name.id)
                  .map(hobby => hobby.hobby)
  ));

const resultSelector = createSelector(
  [namesSelector, hobbiesSelector],
  combineNamesAndHobbies
);

function mapStateToProps(state) 
  return 
    names: namesSelector(state),
    hobbies: hobbiesSelector(state),
    result: resultSelector(state)
  

这将导致您的myApp 组件具有这些props


  names: [
     name: "A", id: "1" ,
     name: "B", id: "2" ,
     name: "C", id: "3" 
  ],
  hobbies: [
     basedId: "1", hobby: "cooking" ,
     basedId: "2", hobby: "reading" ,
     basedId: "3", hobby: "gaming" ,
     basedId: "1", hobby: "reading" 
  ],
  result: [
     name: "A", id: "1", hobby: ["cooking", "reading"] ,
     name: "B", id: "2", hobby: ["reading"] ,
     name: "C", id: "3", hobby: ["gaming"] 
  ]

请注意,hobby 将始终是一个数组,我认为这是最佳实践。 (但您应该将其命名为 hobbies。)

【讨论】:

【参考方案4】:

我同意@xehpuk。通过使用reselect,如果相关状态都没有改变,您可以避免重新计算生成的状态,如果选择器函数被多次调用,例如在渲染循环中,则很有用,因此您不需要手动优化这部分。

为了减少所需的代码,您还可以尝试redux-named-reducers 并改写如下选择器:

const resultSelector = createSelector(
  [namesModule.names, hobbiesModule.hobbies],
  combineNamesAndHobbies
);

这增加了一些开销计算以换取快捷方式

【讨论】:

以上是关于Redux:如何根据其他状态创建状态?的主要内容,如果未能解决你的问题,请参考以下文章

状态管理(1)- Redux

如何根据 Electron 菜单单击更改 Redux 状态?

Redux:动作已成功调度,但未创建状态

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

在动作创建者中访问 Redux 状态?

如何使用 getState 和 redux thunk