web-dev-qa-db-ja.com

createState()にinitialStateを提供しているにもかかわらず、「初期化中に未定義のリデューサー[...]が返される」のはなぜですか?

Redux createStoreメソッドにInitialStateを設定し、対応するInitialStateを2番目の引数として設定しました

ブラウザでエラーが発生しました:

<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.jsconfigeStoremethodを呼び出しました:

 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を設定すると、通常どおりWordを実行できます。何か問題でも?

53
Ice Wilder

createStore()initialState引数は、しばしば人をつまずかせます。アプリケーションの状態を手動で「初期化」する方法として意図されたものではありません。唯一の有用なアプリケーションは次のとおりです。

  • JSON状態ペイロードからサーバーレンダリングアプリを起動します。
  • ローカルストレージに保存された状態からアプリを「再開」する。

initialStateを手動で記述しないでください。ほとんどのアプリでは使用しません。代わりに、リデューサーは常に独自の初期状態を指定する必要があり、initialStateは、既存のシリアル化されたバージョンがある場合にその状態を事前に入力する方法にすぎません。

この答え は正しい:あなたneedは、レデューサーの初期状態を定義する。 createStore()に指定するだけでは不十分であり、コードで初期状態を定義する方法を意図したものではありません。

71
Dan Abramov

同じエラーが発生したが、デフォルトのケースを含めなかった

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

レデューサーが初めて呼び出されたとき、状態は未定義です。その後、初期状態を返す必要があります(これがエラーメッセージに示されています)。初期状態値を定義する通常の方法は、状態パラメーターのデフォルト値を設定することです。

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

posts関数に初期状態がありますが、初期化中には呼び出されません。

23
Florian Cargoet

また、リデューサーで最後にデフォルトの状態を返すようにしてください。これがswitchステートメントのデフォルトであることを忘れることがあります(コードのリファクタリングおよび移動時)。

...
 default:
  return state
10
TechnoTim

レデューサー内で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;
4
duhaime

tl; dr

以下を確認してください。

  • 実際には、アクション/アクションクリエーターを介してリデューサーにデータを渡します!
  • レデューサーは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を返します。これはレデューサーの状態全体を上書きするため、エラーをトリガーするundefinedを返します。

デバッグ

これをデバッグするには、次の場所を調べます。

  • レデューサーが新しい状態を作成する場所。その部分にブレークポイントを配置して、そこにundefinedが届かないことを確認します。
  • データがアクション作成者に渡される場所。
0
totymedli