尽管为createStore()提供了initialState,为什么我在初始化期间得到"Reducer [...]未定义"?

Ice*_*der 57 javascript redux react-redux

我在redux createStore方法中设置了InitialState,并将InitialState作为第二个参数

我在浏览器中出错:

<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>
Run Code Online (Sandbox Code Playgroud)

代码在这里:

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
Run Code Online (Sandbox Code Playgroud)

configeStoreRoot.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>
  )
 }
}
Run Code Online (Sandbox Code Playgroud)

但我想这initateState有些不对劲:

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
Run Code Online (Sandbox Code Playgroud)

但如果我initialState在我的每个减速器中设置它可以正常地说出来.有问题?

Dan*_*mov 75

这种initialState说法createStore()经常使人绊倒.它绝不是一种手动"初始化"应用程序状态的方法.唯一有用的应用是:

  • 从JSON状态有效负载启动服务器呈现的应用程序.
  • "恢复"应用程序从保存到本地存储的状态.

暗示您从不initialState手动编写,在大多数应用程序中甚至不使用它.相反,reducers必须始终指定自己的初始状态,并且initialState只有在您拥有该状态的序列化版本时才能预填充该状态.

所以这个答案是正确的:您需要在reducer中定义初始状态.提供它createStore()是不够的,并不意味着在代码中定义初始状态.

  • 这似乎与Redux文档中的描述相矛盾:http://redux.js.org/docs/recipes/reducers/InitializingState.html (8认同)
  • 从redux docs`所有reducer在初始化时都未定义,因此它们应该被编写为当给定未定义时` (2认同)

Isa*_*Pak 39

我有同样的错误,但我没有包含默认情况

function generate(state={} ,action) {
  switch (action.type) {
    case randomNumber:
      return {
        ...state,
        random: action.payload
      }   
    default: // need this for default case
      return state 
   }
}
Run Code Online (Sandbox Code Playgroud)

  • 是! 这是我的问题. (6认同)

Flo*_*oet 27

当您的Reducer第一次被调用时,状态是未定义的.然后,您必须返回初始状态(这是错误消息告诉您的).定义初始状态值的常用方法是为state参数设置默认值:

function postsBySubreddit(state = {}, action) {}
Run Code Online (Sandbox Code Playgroud)

您在posts函数中有一个初始状态,但在初始化期间不会调用它.


Tec*_*Tim 12

此外,请确保在reducer中最后返回默认状态.有时您可能忘记确保这是switch语句的默认设置(在重构和移动代码时).

...
 default:
  return state
Run Code Online (Sandbox Code Playgroud)

  • 谢谢!这绝对是救生员。说的还不够。 (2认同)
  • 没问题!当我使用自己的复制意大利面(即redux动作)时,我甚至针对相同的问题也找到了相同的答案;) (2认同)

duh*_*ime 5

我刚刚遇到了同样的问题,因为我不小心state在减速器中重新定义了:

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;
Run Code Online (Sandbox Code Playgroud)

要解决此问题,我需要避免重新定义状态:

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;
Run Code Online (Sandbox Code Playgroud)


小智 5

根据 redux 的文档:https ://redux.js.org/basics/reducers

您需要像这样在 switch 函数之外返回状态:


 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
}
Run Code Online (Sandbox Code Playgroud)


小智 5

对我来说,我的减速器就像

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 };
    }
}
Run Code Online (Sandbox Code Playgroud)

而不是(我缺少默认情况)

   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;
        }
    }
Run Code Online (Sandbox Code Playgroud)