【问题标题】:Redux: How to create state based on other state?Redux:如何根据其他状态创建状态?
【发布时间】: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 也可以正常工作,这只是一种优化。

标签: javascript reactjs redux react-redux


【解决方案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
        );
        

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

        【讨论】:

          猜你喜欢
          • 2021-08-04
          • 1970-01-01
          • 2019-04-30
          • 1970-01-01
          • 2019-05-24
          • 1970-01-01
          • 1970-01-01
          • 2017-06-17
          • 2018-08-26
          相关资源
          最近更新 更多