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

IT技术 reactjs redux
2021-05-07 22:01:00

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

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

我认为细粒度操作的优点是事件更准确地反映了用户做了什么(例如用户选择了一个类别),而不是必须查看有效负载才能找出实际更改的内容,但它们都非常相似。

3个回答

这当然是只有您才能根据您对项目的了解才能真正回答的问题。我不认为让动作更细化有任何固有的优势,如果没有,也不值得付出额外的努力。我会有一个通用的FILTER_CHANGED事件,而不用担心能够看到具体的变化——大概这个动作不会很复杂,所以我不会调试这个动作很多。随着过滤器状态变得越来越复杂和多样化,分解动作可能更有意义。不过,默认情况下,我并没有真正看到太多value。

我完全同意内森的回答

我只想补充一点,为了判断动作 A 和 B 是否真的是一个或两个动作,你需要问自己:“如果我改变一些减速器对 A 的react,我是否也需要改变它们对 B 的react? ?”

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

我同意 Dan Abramov 的观点:如果文本和类别在您的界面中高度耦合,只需FETCH_RESULTS将文本和类别作为动作负载触发即可

如果文本输入和类别选择小部件不共享关闭的父组件,则触发FETCH_RESULTS包含文本和类别的a 很复杂(除非将大量props传递到树下......):然后您需要动作粒度。

当需要这种粒度时,我发现一种有用的模式是Saga / Process manager 模式。我在这里写了一点: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;
}

请注意,如果您使用任何时间旅行(记录/重播/撤消/重做/任何)调试器,则在重播操作时应始终禁用 saga,因为您不希望在重播期间分派新操作。

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

关于subjet的讨论很长

编辑

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

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