Should I use one or several action types to represent this async action?

前端 未结 3 1564

I\'m building a front-end for a search system where almost all user actions need to trigger the same async action to re-fetch search results. For example, if a user enters a key

相关标签:
3条回答
  • 2021-02-13 21:23

    I agree with Dan Abramov: if the text and categories are highly coupled in your interface, just fire FETCH_RESULTS with the text and categories as action payload.

    If the text input and categories selection widget do not share a close parent component, it is complicated to fire a FETCH_RESULTS which contains the text and categories (unless passing a lot of props down the tree...): you then need the action granularity.

    One pattern that I have found helpful when such granularity is needed is the Saga / Process manager pattern. I've written a bit about it here: https://stackoverflow.com/a/33501899/82609

    Basically, implementing this on redux would mean there's a very special kind of reducer that can trigger side-effects. This reducer is not pure, but do not have the purpose of triggering React renderings, but instead manage coordination of components.

    Here's an example of how I would implement your usecase:

    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;
    }
    

    Note if you use any time-traveling (record/replay/undo/redo/whatever) debugger, the saga should always be disabled when replaying actions because you don't want new actions to be dispatched during the replay.

    EDIT: in Elm language (from which Redux is inspired) we can perform such effects by "reducing" the effects, and then applying them. See that signature: (state, action) -> (state, Effect)

    There is also this long discussion on the subjet.

    EDIT:

    I did not know before but in Redux action creators can access state. So most problems a Saga is supposed to resolve can often be solved in the action creators (but it creates more unnecessary coupling to UI state):

    function selectCategory(category) {
      return (dispatch, getState) => {
        dispatch({type: "CategorySelected",payload: category});
        dispatch({type: "SearchTriggered",payload: getState().filters});
      }
    }
    
    0 讨论(0)
  • 2021-02-13 21:24

    I fully agree with Nathan’s answer.

    I just want to add that in order to tell whether actions A and B are really one or two actions, you need to ask yourself: “If I change how some reducers react to A, will I also need to change how they react to B?”

    When the handlers change together in the reducer code, it’s likely they should be a single action. When their changes may not affect each other, or if many reducers handle just one of them but not the other, they should probably stay separate.

    0 讨论(0)
  • 2021-02-13 21:39

    This is of course something only you can really answer based on what you know about the project. I don't think that there is any inherent advantage to having the actions be more granular, and if there aren't any, its not worth the extra effort. I would have a generic FILTER_CHANGED event and not worry about being able to see what specifically changed--presumably the action isn't going to be complicated, so I'm not going to be debugging the action a lot. As the filter state becomes more complicated and diverse, it might make more sense to break out the actions. By default though, I don't really see much value.

    0 讨论(0)
提交回复
热议问题