我正在为搜索系统构建一个前端,几乎所有用户操作都需要触发相同的异步操作来重新获取搜索结果。例如,如果用户输入关键字,则我们需要提取/api/search?q=foo
,如果他们稍后选择了我们提取的类别/api/search?q=foo&categoryId=bar
。我最初为FETCH_RESULTS
,SELECT_CATEGORY
,DESELECT_CATEGORY
等设置了单独的操作类型。我为FETCH_RESULTS
创建了一个异步操作创建器,但其他的是同步的。我想的越多,它们最终都需要从后端重新获取结果,并根据后端的响应更新应用程序状态。
对我来说,使用单个异步动作创建器进行任何更改是否合理?或者为每个不同的用户操作(选择关键字,类别或过滤器)使用异步操作创建者会更好吗?
我认为粒度操作的优势在于事件更准确地反映了用户所做的事情(例如用户选择了一个类别)与必须查看有效负载以确定实际发生了什么变化,但它们都非常相似。
答案 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});
}
}