【问题标题】:How to chain redux actions using returned result of the previous action?如何使用前一个操作的返回结果链接 redux 操作?
【发布时间】:2020-10-23 13:15:03
【问题描述】:

我正在使用 React Native 构建一个应用,并使用 Redux 和 redux-persist 充当设备数据库。

问题的症结在于,我如何返回一个 redux 操作的结果,然后用这些数据调度另一个操作?请继续阅读以了解更多细节。

用户可以创建自定义习惯类型。这样做时,我会发送一个动作来在商店中创建一个习惯类型(例如“跑步”)。此操作会为习惯类型生成一个新的唯一 UUID。然后我想将这个新创建的习惯类型添加到例程中(例如“早上例程”),因此我需要接收回习惯类型的 UUID 并调用另一个调度将其添加到例程中。

我使用immer 来简化reducer 中的状态操作,并使用以下代码(简化示例):

import produce from "immer";

const userReducer = (state = initialState, action) => {
  switch (action.type) {
    case ADD_CUSTOM_HABIT_TYPE: {
      return produce(state, draftState => {
        const newHabitType = {
          id: generateUuid(),
          name,
        };

        draftState.customHabitTypes.push(newHabitType);

        return draftState;
      });
    }
  }
};

然后我将它发送到我的组件中,就像这样(简化):

dispatch({
  type: ADD_CUSTOM_HABIT_TYPE,
  name: "running",
});

在创建这种新习惯类型之后,我怎么能说调度另一个动作并将其添加到我的例程中?

我查看了redux-thunkredux-saga,并花了几个小时阅读这些内容并试图让redux-thunk 工作,但无济于事。我相信这一定很简单,但我是空白的,所以也许其他人也是如此,因此这篇文章。

【问题讨论】:

    标签: reactjs redux redux-saga redux-thunk redux-persist


    【解决方案1】:

    一个非常简单的解决方案是在调度操作之前生成唯一 id。

    示例

    const newHabitType = {
      id: generateUuid(),
      name,
    };
    
    dispatch({
      type: ADD_CUSTOM_HABIT_TYPE,
      habit: newHabitType,
    });
    
    dispatch({
      type: ADD_CUSTOM_HABIT_TO_ROUTINE,
      habit: newHabitType.id,
    });
    

    优点

    1. 您不再需要将操作本身链接起来,只需按顺序分派它们即可。
    2. 这保留了最重要的 Redux 准则之一:您的 reducer 不应该有任何副作用(在您的情况下,生成一个随机 id)。 reference

    缺点

    1. 如果您在多个地方创建新习惯,则必须在您发送操作的每个地方生成唯一 ID。这可能会导致重复代码。解决方案是将创建习惯的整个逻辑封装到单个组件中,然后在任何地方重用该组件。

    【讨论】:

    • 嘿哈立德,感谢您的回复!我认为你是对的,我自己也在思考这个问题,但后来你提到的 Con 出现了,所以我觉得必须有一种 redux 的做事方式来启用 ADD_CUSTOM_HABIT_TYPE 动作来完全封装创建这种新的习惯类型(包括UUID 一代)。从您引用的文档看来,这不是 redux 方式。我将了解如何根据最适合的方式将逻辑封装在函数或组件中。感谢您在此处提供说明。
    • 这里只是一个更新,使用redux-thunk 创建一个生成UUID 并将其传递给两个调度的函数(基本上是 Khaled 上面的确切代码,只是包装在一个 thunk 中)工作很好,并允许我调用dispatch(ADD_CUSTOM_HABIT_TYPE_THUNK({ habit }),thunk 处理 UUID 生成和两个 redux 操作调用。
    【解决方案2】:

    Action 本身不返回数据,它们只是根据 reducer 中定义的规则改变存储的对象。两种可能的解决方案:

    选项A,创建复合动作。

    const compositeAction = args => {
        return dispatch => {
            return someAsyncCall(args).then(response => {
               dispatch(addCustomHabitat(response))
               dispatch(followUpAction())
            }
        }
    }
    
    const addCustomHabitat = response => {
        return {
            type: "ADD_CUSTOM_HABIT_TYPE",
            data: response
        }
    }
    
    const followUpAction = () => {
        ...another action...
    }
    

    选项B,将第一个动作的结果通过react-redux连接到调度组件,传递给第二个动作。

    import {connect} from 'react-redux';
    
    const MyReactComponent = props => {
        dispatch(addCustomHabitatTypeAction());
    
        if(props.customHabitatType !== undefined)
            dispatch(followUpAction(props.customHabitatType());
    
        return (
           ...JSX here...
        );
    }
    
    const mapStateToProps = state => {
        return {
            customHabitatType: state.userReducer.customHabitatType
        }
    }
    
    connect(mapStateToProps)(MyReactComponent);
    

    我希望这会有所帮助!请原谅我的缩写代码,如果您有任何问题,请告诉我。

    【讨论】:

    • 感谢 Daly 的回复!让我试着理解一些事情。在Option A 中,我认为您建议我从操作中移出UUID 创建,将其放入一个新函数someAsyncCall,然后将这个新的UUID 传递给ADD_CUSTOM_HABIT_TYPEADD_HABIT_TYPE_TO_ROUTINE 操作?在Option B,我不太清楚。这个组件如何知道发生了什么变化?由于可能有 5 种现有的自定义习惯类型,更改后将有 6 种,我是否需要手动 diff 才能获得新的?不知道这应该如何工作。谢谢!
    • 对于选项 A,如果 UUID 生成必须异步执行,那么是的,您可以使用该方法。对于选项 B,每次操作更改 cusomHabitatType 的值时都会重新渲染组件。然后,您可以在组件内使用条件来根据该道具的值做出进一步的决定。老实说,尽管我认为 Khaled 的解决方案会通过保持逻辑简单来更好地为您服务。 :) 这些是更重量级的选项。
    • 感谢 Daly,感谢这里的想法和建议!意识到“redux 方式”是没有副作用的,我想我会像 Khaled 的解决方案一样重构 UUID 创建。
    猜你喜欢
    • 2017-05-03
    • 1970-01-01
    • 1970-01-01
    • 2019-08-31
    • 2018-01-31
    • 2017-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多