【问题标题】:Why do I get “Reducer [...] returned undefined during initialization” despite providing initialState to createStore()?尽管向 createStore() 提供了 initialState,为什么我得到“Reducer [...] 在初始化期间返回未定义”?
【发布时间】:2016-08-05 18:29:42
【问题描述】:

我在我的 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>

代码在这里:

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

我在Root.js 中调用了configeStoremethod:

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

但我猜这个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

但是如果我在我的每个子减速器中设置initialState,它可以正常运行。有问题?

【问题讨论】:

    标签: javascript redux react-redux


    【解决方案1】:

    createStore() 中的 initialState 参数经常让人绊倒。它从来都不是一种手动“初始化”应用程序状态的方法。它唯一有用的应用是:

    • 从 JSON 状态负载启动服务器呈现的应用程序。
    • “恢复”应用程序从保存到本地存储中的状态。

    这意味着您永远不会手动编写initialState,并且在大多数应用程序中您甚至都不使用它。相反,reducer 必须始终指定自己的初始状态,initialState 只是在您拥有现有序列化版本时预填充该状态的一种方式。

    所以this answer 是正确的:你需要在你的reducer 中定义初始状态。将其提供给 createStore() 是不够的,也不是在代码中定义初始状态的一种方式。

    【讨论】:

    • 这似乎与 Redux 文档中的描述相矛盾:redux.js.org/docs/recipes/reducers/InitializingState.html
    • 来自 redux 文档All reducers are passed undefined on initialization, so they should be written such that when given undefined
    • @aks 的回答其实更合适。我有一个类似的问题,但可能是由同样的问题引起的。我发现,根据 redux 文档,reducers 应该将 undefined 状态作为参数处理,并且应该在这些情况下返回初始状态。
    【解决方案2】:

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

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

    【讨论】:

    • 是的!这是我的问题。
    • 通常 Eslint 或 Prettier 会接受我没有默认案例的事实。所以谢谢!这很容易被忽视。
    • 是的,我也忘了添加默认情况。谢谢。
    • 我回来了,是的,它又解决了。我应该停止忘记事情。
    • 这也是我的问题。
    【解决方案3】:

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

    ...
     default:
      return state
    

    【讨论】:

    • 谢谢!这绝对是救命稻草。说的不够。
    • 没问题!当使用我自己的复制意大利面时,我什至针对相同的问题得到了相同的答案,即 redux 操作;)
    【解决方案4】:

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

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

    posts 函数中有一个初始状态,但在初始化期间没有调用它。

    【讨论】:

    • 但我在第二个参数的 createStore 方法中有一个初始状态
    • 你是对的。看起来其中一个键与减速器不匹配:selectedsubreddit => selectedSubreddit。会不会是错误的根源?
    • 早上运行正常,下午就不行了,浏览器报同样的错误。我从不更改我的代码,但它仍然显示相同的错误
    【解决方案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;
    

    为了解决这个问题,我需要避免重新定义状态:

    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;
    

    【讨论】:

      【解决方案6】:

      根据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
      }
      

      【讨论】:

      • 如果我们在 switch 语句之外返回状态,它将变得无法访问
      • 嗯,我在关注文档。现在应该更新了。感谢您的关注
      【解决方案7】:

      确保你在最后有一个默认状态

      ...
      default:
        return state;
      

      【讨论】:

        【解决方案8】:

        tl;博士

        确保:

        • 您实际上是通过动作/动作创建器将数据传递给减速器!
        • reducer 不返回 undefined

        示例

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

        在这种情况下,如果您不小心通过动作创建器将undefined 传递给action.data,那么react-addons-update 将返回来自该变量的undefined。由于这会覆盖整个 reducer 状态,因此它将返回将触发错误的 undefined

        调试

        你可以通过检查这些地方来调试它:

        • reducer 创建新状态的位置。在该部分放置一个断点,以确保您不会在那里收到或返回undefined
        • 数据传递给操作创建者的位置。

        【讨论】:

          【解决方案9】:

          对我来说,我有我的减速器

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

          而不是(我错过了默认情况)

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

          【讨论】:

            猜你喜欢
            • 2017-09-04
            • 2019-03-31
            • 2017-10-18
            • 2021-11-05
            • 1970-01-01
            • 2021-07-02
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多