91

I had set InitialState in my redux createStore method ,and I corresponding InitialState as second arguments

I got a error in browser:

<code>Uncaught Error: Reducer "postsBySubreddit" returned undefined during initialization. If the state passed to the reducer is undefined, you must explicitly return the initial state. The initial state may not be undefined.</code>

code is here:

import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
import createLogger from 'redux-logger'
import rootReducer from '../reducers/reducers'
import Immutable from 'immutable'
const loggerMiddleware = createLogger()
//const initialState=0
function configureStore() {
    return createStore(
    rootReducer,
     {postsBySubreddit:{},selectedSubreddit:'reactjs'},
     applyMiddleware(
     thunkMiddleware,
    loggerMiddleware
  )
 )
}
  export default configureStore

and I invoked configeStoremethod in Root.js:

 import React, { Component } from 'react'
 import { Provider } from 'react-redux'
 import configureStore from '../store/configureStore'
 import AsyncApp from './AsyncApp'
 import Immutable from 'immutable'
 const store = configureStore()
 console.log(store.getState())
 export default class Root extends Component {
 render() {
   return (
     <Provider store={store}>
       <AsyncApp />
     </Provider>
  )
 }
}

but I guess this initateState has something wrong:

import { combineReducers } from 'redux'
import {reducerCreator} from '../utils/creator'
import Immutable from'immutable'
import {SELECT_SUBREDDIT, INVALIDATE_SUBREDDIT ,REQUEST_POSTS, RECEIVE_POSTS} from '../actions/action'
let initialState=Immutable.fromJS({isFetching: false, didInvalidate: false,items:[]})

function selectedSubreddit(state, action) {
  switch (action.type) {
  case SELECT_SUBREDDIT:
    return action.subreddit
  default:
    return state
  }
}
function postsBySubreddit(state, action) {
  switch (action.type) {
    case INVALIDATE_SUBREDDIT:
    case RECEIVE_POSTS:
    case REQUEST_POSTS:
      return Object.assign({}, state, {
        [action.subreddit]: posts(state[action.subreddit], action)
      })
    default:
      return state
  }
}
function posts(state=initialState,action) {
  switch (action.type) {
    case INVALIDATE_SUBREDDIT:
      return state.merge({
        didInvalidate: true
      })
    case REQUEST_POSTS:
      return state.merge({
        isFetching: true,
        didInvalidate: false
      })
    case RECEIVE_POSTS:
      return state.merge({
        isFetching: false,
        didInvalidate: false,
        items: action.posts,
        lastUpdated: action.receivedAt
      })
    default:
      return state 
    }
}

const rootReducer = combineReducers({
  postsBySubreddit,
 selectedSubreddit
})
export default rootReducer

but if I set initialState in my every sub reducer it can did word normally. Something wrong?

Dan Abramov
  • 264,556
  • 84
  • 409
  • 511
Ice Wilder
  • 941
  • 1
  • 7
  • 7

11 Answers11

107

I had the same error but I didn't include a default case

function generate(state={} ,action) {
  switch (action.type) {
    case randomNumber:
      return {
        ...state,
        random: action.payload
      }   
    default: // need this for default case
      return state 
   }
}
Isaac Pak
  • 4,467
  • 3
  • 42
  • 48
94

The initialState argument in createStore() is often tripping people. It was never meant as a way to “initialize” your application state manually. The only useful applications for it are:

  • Booting up server rendered app from JSON state payload.
  • “Resuming” the app from a state saved into local storage.

It is implied that you never write initialState manually and in most apps you don’t even use it. Instead, reducers must always specify their own initial state, and initialState is just a way to prefill that state when you have an existing serialized version of it.

So this answer is correct: you need to define the initial state in your reducer. Supplying it to createStore() is not enough, and is not meant to be a way to define the initial state in code.

Community
  • 1
  • 1
Dan Abramov
  • 264,556
  • 84
  • 409
  • 511
  • 12
    This seems to contradict the description in Redux documentation: http://redux.js.org/docs/recipes/reducers/InitializingState.html – martin Jun 25 '17 at 13:36
  • 2
    From redux docs `All reducers are passed undefined on initialization, so they should be written such that when given undefined` – aks Aug 07 '17 at 07:10
  • @aks answer is actually more appropriate. I have had a similar issue but probably caused by the same problem. I discovered, that reducers are supposed to handle `undefined` states as parameter and are suppose to return the initial state in those cases, as per redux documentation. – ggwzrd Jun 18 '21 at 17:00
  • @martin I struggle to understand why would Redux ever pass `undefined` as the value for a reducer if it gets the initial value. – Robo Robok May 14 '23 at 17:27
46

Also, make sure that you are returning the default state last in your reducer. Sometimes you can forget to make sure this is the default for your switch statement (when refactoring and moving around code).

...
 default:
  return state
TechnoTim
  • 3,065
  • 1
  • 23
  • 28
33

When your reducers are called for the first time, state is undefined. You must then return the initial state (that's what the error message is telling you). The usual way of defining the initial state value is to set a default value for the state parameter:

function postsBySubreddit(state = {}, action) {}

You have an initial state in the posts function but it is not called during initialization.

Florian Cargoet
  • 871
  • 1
  • 7
  • 14
  • but I have a initial state in createStore method on second arguments – Ice Wilder Apr 14 '16 at 10:47
  • You're right. It looks like one of the keys doesn't match the reducer: selectedsubreddit => selectedSubreddit. Could it be the source of error? – Florian Cargoet Apr 14 '16 at 11:12
  • In the morning it runs correctly, but in this afternoon it can't work and browser show same error . I never change my code but it stills show same error – Ice Wilder Apr 15 '16 at 10:32
9

I just hit this same snag because I accidentally redefined state inside my reducer:

const initialState = {
  previous: true,
  later: true
}

const useTypeReducer = (state = initialState, action) => {
  switch (action.type) {
    case 'TOGGLE_USE_TYPES':
      let state = Object.assign({}, state);   // DON'T REDEFINE STATE LIKE THIS!
      state[action.use] = !state[action.use];
      return state;

    default:
      return state;
  }
}

export default useTypeReducer;

To fix the problem I needed to refrain from redefining state:

const initialState = {
  previous: true,
  later: true
}

const useTypeReducer = (state = initialState, action) => {
  switch (action.type) {
    case 'TOGGLE_USE_TYPES':
      let _state = Object.assign({}, state);   // BETTER
      _state[action.use] = !state[action.use];
      return _state;

    default:
      return state;
  }
}

export default useTypeReducer;
duhaime
  • 25,611
  • 17
  • 169
  • 224
6

as per the documentation of redux: https://redux.js.org/basics/reducers

you need to return state out side of switch function like this:


 function posts(state=initialState,action) {
  switch (action.type) {
    case INVALIDATE_SUBREDDIT:
      return state.merge({
        didInvalidate: true
      })
    case REQUEST_POSTS:
      return state.merge({
        isFetching: true,
        didInvalidate: false
      })
    case RECEIVE_POSTS:
      return state.merge({
        isFetching: false,
        didInvalidate: false,
        items: action.posts,
        lastUpdated: action.receivedAt
      })
    default:
      return state 
    }

//here you should put the return state
return state
}
5

For me, I had my reducer like

import * as actions from '../actions/budgetActions';

export const initialState = {
    budget: {},
    loading: false,
    hasErrors: false,
}

export default function budgetReducer(state = initialState, action) {

    switch (action.type) {
        case actions.GET_BUDGET:
            return { ...state, loading: true };
        case actions.GET_BUDGET_SUCCESS:
            return { budget: action.payload, loading: false, hasErrors: false };
        case actions.GET_BUDGET_FAILURE:
            return { ...state, loading: false, hasErrors: true };
    }
}

instead of (I was missing a default case)

   import * as actions from '../actions/budgetActions';
    
    export const initialState = {
        budget: {},
        loading: false,
        hasErrors: false,
    }
    
    export default function budgetReducer(state = initialState, action) {
    
        switch (action.type) {
            case actions.GET_BUDGET:
                return { ...state, loading: true };
            case actions.GET_BUDGET_SUCCESS:
                return { budget: action.payload, loading: false, hasErrors: false };
            case actions.GET_BUDGET_FAILURE:
                return { ...state, loading: false, hasErrors: true };
            default:
                return state;
        }
    }
3

ensure you have a default state at the end like so

...
default:
  return state;
Edwin O.
  • 4,998
  • 41
  • 44
2

tl;dr

Make sure that:

  • You actually pass data to your reducer via the action/action creator!
  • The reducer doesn't return undefined.

Example

const reducer = (state = initialState, action) => {
  switch (action.type) {
    case 'SET_DATA':
      debugger // Place a breakpoint for investigation
      const result = update(state, {$set: action.data});
      // `result` is `undefined` if `action.data` is too
      return result;
    default:
      return state;
  }
}

In this case, if you accidentally pass undefined to action.data via your action creator then react-addons-update will return the undefined that came from that variable. Since this overwrites the whole reducer state it will return that undefined that will trigger the error.

Debugging

You can debug this by examining these places:

  • Where the reducer creates the new state. Place a breakpoint into that part to make sure you don't receive or return undefined there.
  • Where the data is passed to the action creator.
totymedli
  • 29,531
  • 22
  • 131
  • 165
1

in case of createStore() make sure that the reducer is initialised

Example

const init = {
  state: true,
  value: 0
}

const value = (prev = init, action) => {
    switch (action.type) {
        case 'increment':
            return {
                ...prev,
                value: prev.value + 1
            }
        case 'decrement':
            return {
                ...prev,
                value: prev.value - 1
            }
        default:
            return { ...prev }
    }
}

in the case of the redux toolkit inside the createSlice() object make sure that the key initialState is used and initialized and named as exactly initialState.

Example

import { createSlice } from "@reduxjs/toolkit"
const valueSlice = createSlice({
 name: 'value',
    initialState: 0,
      reducers: {
        increment: (state, action) => {
            return state + 1;
        },
        decrement: (state, action) => {
            return state - 1;
        }
    }
})
Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175
  • wow dude.. thanks for your help, i need this answer.. maybe react-redux without toolkit is outdated but i need this for my study redux basic.. thank you – Heru Wijayanto Oct 13 '22 at 05:21
0
 const firstReducer = (state = 0) => {   
        switch (0) {
     case 0:
       return state + 1;
     default:// add this default in user reducer functions
       return state;   
 } };  export default firstReducer;
Willie Cheng
  • 7,679
  • 13
  • 55
  • 68