我应该使用一种或多种操作类型来表示此异步操作吗?

时间:2015-11-10 19:12:56

标签: reactjs redux

我正在为搜索系统构建一个前端,几乎所有用户操作都需要触发相同的异步操作来重新获取搜索结果。例如,如果用户输入关键字,则我们需要提取/api/search?q=foo,如果他们稍后选择了我们提取的类别/api/search?q=foo&categoryId=bar。我最初为FETCH_RESULTSSELECT_CATEGORYDESELECT_CATEGORY等设置了单独的操作类型。我为FETCH_RESULTS创建了一个异步操作创建器,但其他的是同步的。我想的越多,它们最终都需要从后端重新获取结果,并根据后端的响应更新应用程序状态。

对我来说,使用单个异步动作创建器进行任何更改是否合理?或者为每个不同的用户操作(选择关键字,类别或过滤器)使用异步操作创建者会更好吗?

我认为粒度操作的优势在于事件更准确地反映了用户所做的事情(例如用户选择了一个类别)与必须查看有效负载以确定实际发生了什么变化,但它们都非常相似。

3 个答案:

答案 0 :(得分:11)

当然,根据您对项目的了解,您可以真正回答这个问题。我认为让行动更加细化并没有任何固有的优势,如果没有,那就不值得付出额外的努力。我会有一个通用的FILTER_CHANGED事件而不用担心能看到什么特别改变 - 大概这个动作不会很复杂,所以我不打算大量调试动作。随着过滤器状态变得更加复杂和多样化,打破行动可能更有意义。但默认情况下,我并没有真正看到太多价值。

答案 1 :(得分:9)

我完全同意Nathan’s answer

我只是想补充一点,为了判断A和B行动是否真的是一两个动作,你需要问自己:“如果我改变一些减速器对A的反应,我还需要改变它们的方式对B做出反应?“

当处理程序在reducer代码中一起更改时,它们可能应该是单个操作。当他们的变化可能不会相互影响,或者如果许多减速器只处理其中一个但不处理另一个,它们应该保持分开。

答案 2 :(得分:2)

我同意Dan Abramov的观点:如果文字和类别在您的界面中高度耦合,只需将FETCH_RESULTS与文本和类别作为动作有效负载进行激活。

如果文本输入和类别选择小部件不共享一个紧密的父组件,则触发包含文本和类别的FETCH_RESULTS是很复杂的(除非在树中传递很多道具......) :然后您需要操作粒度。

当需要这样的粒度时,我发现有一种模式是Saga /进程管理器模式。我在这里写了一些:https://stackoverflow.com/a/33501899/82609

基本上,在redux上实现这一点意味着有一种非常特殊的减速器可以触发副作用。这个reducer不是纯粹的,但是没有触发React渲染的目的,而是管理组件的协调。

以下是我将如何实现您的用例的示例:

function triggerSearchWhenFilterChangesSaga(action,state,dispatch) {
    var newState = searchFiltersReducer(action,state);
    var filtersHaveChanged =  (newState !== state);
    if ( filtersHaveChanged )  {
        triggerSearch(newFiltersState,dispatch)
    }
    return newState;
}


function searchFiltersReducer(action,state = {text: undefined,categories: []}) {
    switch (action.type) {
        case SEARCH_TEXT_CHANGED:
            return Object.assign({}, state, {text: action.text});
            break;
        case CATEGORY_SELECTED:
            return Object.assign({}, state, {categories: state.categories.concat(action.category) });
            break;
        case CATEGORY_UNSELECTED:
            return Object.assign({}, state, {categories: _.without(state.categories,action.category) });
            break;
    }
    return state;
}

请注意,如果您使用任何时间旅行(记录/重放/撤消/重做/无论)调试器,则在重放操作时应始终禁用该传奇,因为您不希望在重播期间调度新操作

编辑:在Elm语言中(Redux受到启发)我们可以通过“减少”效果来执行此类效果,然后应用它们。查看该签名:(state, action) -> (state, Effect)

子喷气机上还有long discussion

修改

我以前不知道但是在Redux中,动作创建者可以访问状态。因此,Saga应该解决的大多数问题通常可以在动作创建者中解决(但它会创建更多不必要的与UI状态的耦合):

function selectCategory(category) {
  return (dispatch, getState) => {
    dispatch({type: "CategorySelected",payload: category});
    dispatch({type: "SearchTriggered",payload: getState().filters});
  }
}