简体   繁体   中英

Why does my Redux reducer refactor fail?

Here is an example of what I'm attempting to do:

const setView = state => payload => ({ ...state, view: payload });

const reducers = {
  SET_VIEW: setView
};

export default (state = initialState, action) => reducers[action.type](state)(action.payload);

Unfortunately, I get the following error:

Uncaught TypeError: reducers[action.type] is not a function

What exactly am I doing wrong? reducers is an object literal with functions.

This is quite an obscure issue actually. The reason is because, per the Redux documentation on createStore :

When a store is created, Redux dispatches a dummy action to your reducer to populate the store with the initial state. You are not meant to handle the dummy action directly. Just remember that your reducer should return some kind of initial state if the state given to it as the first argument is undefined, and you're all set.

And this dummy action mentioned by the documentation just so happens to be this line the source :

dispatch({ type: ActionTypes.INIT })

Here, ActionTypes.INIT is essentially the string @@redux/INIT followed by a random string of numbers and periods.

Thus, when you create the store with createStore , a dummy action is dispatched to your reducer, and that action type does not exist in your reducers object , thus you get the error that undefined isn't a function. That's why you always have a default case in your reducer. For example, with a switch statement, you always return state as a default case:

switch(action.type) {
  …
  default:
    return state;
}

The default case allows for the catching of actions such as the dummy action dispatched by Redux itself. The same principle applies to your code:

export default (state = initialState, action) => reducers[action.type] ? reducers[action.type](state)(action.payload) : state;

This checks to see if the reducer actually exists in the reducers object. If it does, it calls the reducer. If not, just like in the default case, the state is just returned.

@Li357 is correct as to why you are getting that error. I would like to propose an alternative solution to the problem:

const setView = state => payload => ({ ...state, view: payload });

const reducers = {
  SET_VIEW: setView
};

const noop = state => () => state;

export default (state = initialState, action) => (reducers[action.type] || noop)(state)(action.payload);

The trick here is the (reducers[action.type] || noop) part, which will use a noop handler if there isn't a known handler for the action type. All it does is return the current state.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM