6
votes

I'm working on my first React/Redux project. Everything was going fine, and then I tried to create a new reducer. I thought it was a pretty simple one, but when I load the page I get an error "Reducer X returned undefined during initialization." The trace says this is happening in combineReducers(). I found a couple of similar questions, but they didn't solve the issue.

On this question: Why do I get “Reducer [...] returned undefined during initialization” despite providing initialState to createStore()?

The issue was that they were using initialState in createStore(), which I'm not doing.

On this question: Why does my Redux reducer think my state is undefined?

The problem was a missing default return value in the reducer, which I have.

My reducer code is below. I have a console.log() at the beginning and it is not being called at all.

reducers/reducer_which_sorter.js

import { SORT_CAMPERS } from '../actions/index';

export default function(state = null, action) {
    console.log("action is", action);
    switch(action.which) {
        case 'recent':
        case 'alltime':
            return action.which;
            break;
        default:
            return state;
    }

    return state;
}

reducers/index.js

import { combineReducers } from 'redux';
import Campers from './reducer_camper_list';
import ActiveSorter from './reducer_which_sorter';

const rootReducer = combineReducers({
  campers: Campers,
  activeSorter: ActiveSorter
});

export default rootReducer;

Everything compiles fine. No errors from webpack. I've double, triple and quadruple checked my file paths. I don't see any typos. Can anyone see something I am missing here?

4
Put a breakpoint on a line that throws an error and refresh the page. Then when it breaks in the debugger - you have the complete details on what happened.zerkms

4 Answers

5
votes

A Redux requirement is to use a type property on actions, not which, as you've done in your example. Since your console.log check isn't called, the problem could be in your other reducer (not shown in your question.)

The reason this matters is that combineReducers is calling your reducer functions with a special INIT action type:

const initialState = reducer(undefined, { type: ActionTypes.INIT })

Source: https://github.com/reactjs/redux/blob/master/src/combineReducers.js

Your function, since it switches on 'action.which' instead of 'action.type', fails to return anything except undefined.

3
votes

I have a console.log() at the beginning and it is not being called at all.

It's strange, because the reducer function must have been called at least once with @@INIT action.

Try to test if your rootReducer is called at all. Change it to:

const rootReducer = (state = {}, action) => {
  console.log('Test if rootReducer is ever called')

  return {
    campers: Campers(state.campers, action),
    activeSorter: ActiveSorter(state.activeSorter, action)
  }
}

If it's not being called, then the problem is somewhere higher, not in the reducer itself.

0
votes

This error will happen when your return statement is not returning a valid object, When you see this error always check what your reducer is returning.

One more interesting scenario, I was creating a basic redux app, and incurred the same error, in my case I was just returning an array of objects and got the same error, everything was fine I banged my head against the wall and finally found that it was due to a very very basic javascript mistake.

return statements check for values in the same line and does not consider the line next.

export default function(){
  return     // '[' should be here not in the next line ** basic mistake****
  [
      {
        id: 1,
        name: "sachin",
        centuries: "100",
        country: "India"
      },
      {
        id: 2,
        name: "ganguly",
        centuries: "42",
        country: "India"
      },
      {
        id:3,
        name: "dravid",
        centuries: "40",
        country: "India"
      }
    ]

}

As javascript returned undefined the combineReducers function was not receiving the expected object instead undefined.

Makes sure you have proper return statements

0
votes

When putting together a reducer, in your switch statement you are going to pass in an action.type from the action creators type property. There is not which property in an action creator that it will pass to a reducer.

Keep in mind action creators are always passing plain JavaScript action objects over to the reducers. Even when its a function that gets received by a middleware such as Redux-Thunk, that middleware will eventually modify the action creators function into an action object to be sent on to all your reducers.

Also, your case needs to be the value of the action type itself. So lets say your action creator is asynchronous, its fetching data from some endpoint and it looks like so:

export const allTime = () => async dispatch => {
  const response = await someEndPoint.get("/all");
  dispatch({ type: 'ALL_TIME', payload: response })
};

Aside from your switch being passed in the actions type property, the case has to be the value of that type property, in this example 'ALL_TIME' and you want to return the action creator's payload, if you are telling me that the payload property is returning which, then the above would look like this:

export const allTime = () => async dispatch => {
  const which = await someEndPoint.get("/all");
  dispatch({ type: 'ALL_TIME', payload: which })
};

Otherwise, you will most probably be returning a payload: response.

So instead of:

export default function(state = null, action) {
    console.log("action is", action);
    switch(action.which) {
        case 'recent':
        case 'alltime':
            return action.which;
            break;
        default:
            return state;
    }

    return state;
}

try:

export default function(state = null, action) {
    console.log("action is", action);
    switch(action.type) {
        case 'ALL_TIME':
            return action.response;
        default:
            return state;
    }
};

See what the above refactor does for you, I believe it will put you closer to your goal if not solve the error. If you need to start over just keep in mind the rules of JavaScript, a function must always return some value, so you may need to go back to:

export default () => {
   return 123;
};

If you implement that your error message will definitely go away and you can use that time to consider the answers to your question here on SO and introspect a solution from it. Heck you can even implement:

export default () => {
   return ‘howdy’;
};

You can return an empty array:

export default () => {
   return [];
};

You can return an object:

export default () => {
   return {};
};

You can even return the value of null like so:

export default () => {
   return null;
};

and your error message will go away. The problem is right now with your logic, its the equivalent of doing this:

export default () => {
  return undefined;
}

And that's unacceptable in the world of reducers.

You see the error says returned undefined during initialization. So when Redux first boots up its going to run each of your reducers one time.

So that means the first time your reducer was ran it returned the value of undefined, your reducer can never ever return undefined, whether at initialization or at some point in the future when an action has been dispatched.

So with reducers you must always return any value besides undefined.