Warum wird "Reducer […] während der Initialisierung undefiniert zurückgegeben", obwohl createStore () initialState bereitgestellt wird?

69

Ich hatte InitialState in meiner redux createStore-Methode festgelegt und InitialState als zweites Argument angegeben

Ich habe eine Fehlermeldung im Browser erhalten:

<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 ist hier:

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

und ich habe die configeStoreMethode aufgerufen 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>
  )
 }
}

aber ich denke das initateStatehat etwas falsches:

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

aber wenn ich initialStatein jedem Sub-Reduzierer setze , kann es normal sprechen. Etwas stimmt nicht?

Ice Wilder
quelle

Antworten:

81

Das initialStateArgument in createStore()stolpert oft Menschen. Es war nie als eine Möglichkeit gedacht, Ihren Anwendungsstatus manuell zu "initialisieren". Die einzigen nützlichen Anwendungen dafür sind:

  • Starten der vom Server gerenderten App über die Nutzdaten des JSON-Status.
  • "Fortsetzen" der App aus einem Status, der im lokalen Speicher gespeichert ist.

Es wird vorausgesetzt, dass Sie niemals initialStatemanuell schreiben und in den meisten Apps nicht einmal verwenden. Stattdessen müssen Reduzierer immer ihren eigenen Anfangszustand angeben. initialStateDies ist nur eine Möglichkeit, diesen Zustand vorab zu füllen, wenn Sie über eine vorhandene serialisierte Version davon verfügen.

Also diese Antwort richtig ist: Sie müssen den Ausgangszustand in Ihrem Minderer definieren. Die Bereitstellung createStore()reicht nicht aus und ist nicht als Möglichkeit gedacht, den Anfangszustand im Code zu definieren.

Dan Abramov
quelle
9
Dies scheint der Beschreibung in der Redux-Dokumentation zu widersprechen: redux.js.org/docs/recipes/reducers/InitializingState.html
Martin
1
Von Redux DocsAll reducers are passed undefined on initialization, so they should be written such that when given undefined
aks
66

Ich hatte den gleichen Fehler, aber keinen Standardfall angegeben

function generate(state={} ,action) {
  switch (action.type) {
    case randomNumber:
      return {
        ...state,
        random: action.payload
      }   
    default: // need this for default case
      return state 
   }
}
Isaac Pak
quelle
8
Ja! Das war mein Problem.
Rob Grant
1
Normalerweise würden Eslint oder Prettier die Tatsache aufgreifen, dass ich keinen Standardfall hatte. Also vielen Dank! Dies ist leicht zu übersehen.
Karl Taylor
1
Ja, ich habe vergessen, auch den Standardfall hinzuzufügen. Vielen Dank.
Allan Mwesigwa
1
Und ich bin zurück und yay es hat es wieder gelöst. Ich sollte aufhören, Dinge zu vergessen.
Rob Grant
1
Das war auch mein Problem.
iaq
32

Stellen Sie außerdem sicher, dass Sie den Standardstatus zuletzt in Ihrem Reduzierer zurückgeben. Manchmal können Sie vergessen, sicherzustellen, dass dies die Standardeinstellung für Ihre switch-Anweisung ist (beim Umgestalten und Bewegen von Code).

...
 default:
  return state
TechnoTim
quelle
2
DANKESCHÖN! Dies war ein absoluter Lebensretter. Kann es nicht genug sagen.
anon58192932
2
Kein Problem! Ich bin sogar auf dieselbe Antwort für dasselbe Problem
gestoßen,
29

Wenn Ihre Reduzierungen zum ersten Mal aufgerufen werden, ist der Status undefiniert. Sie müssen dann den Ausgangszustand zurückgeben (das sagt Ihnen die Fehlermeldung). Die übliche Methode zum Definieren des Anfangszustandswerts besteht darin, einen Standardwert für den Zustandsparameter festzulegen:

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

Sie haben einen Anfangszustand in der postsFunktion, der jedoch während der Initialisierung nicht aufgerufen wird.

Florian Cargoet
quelle
Aber ich habe einen Anfangszustand in der createStore-Methode für zweite Argumente
Ice Wilder
Du hast recht. Es sieht so aus, als ob einer der Schlüssel nicht mit dem Reduzierer übereinstimmt: selectedsubreddit => selectedSubreddit. Könnte es die Fehlerquelle sein?
Florian Cargoet
Am Morgen läuft es richtig, aber am Nachmittag kann es nicht funktionieren und der Browser zeigt den gleichen Fehler an. Ich ändere nie meinen Code, aber es zeigt immer noch den gleichen Fehler
Ice Wilder
7

Ich habe gerade den gleichen Haken getroffen, weil ich versehentlich statein meinem Reduzierer neu definiert habe:

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;

Um das Problem zu beheben, musste ich den Zustand nicht neu definieren:

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
quelle
4

gemäß der Dokumentation von Redux: https://redux.js.org/basics/reducers

Sie müssen den Status außerhalb der Schalterfunktion wie folgt zurückgeben:


 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
}
Khorram Bin Salim Khondkar
quelle
Wenn wir den Status außerhalb der switch-Anweisung zurückgeben, wird er nicht mehr erreichbar
James
hmm ich habe die dokumentation befolgt. Es wurde wahrscheinlich jetzt aktualisiert. Vielen Dank für die Kenntnisnahme
Khorram Bin Salim Khondkar
0

tl; dr

Stelle sicher das:

  • Sie geben tatsächlich Daten über die Aktion / den Aktionsersteller an Ihren Reduzierer weiter!
  • Der Reduzierer kehrt nicht zurück undefined.

Beispiel

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 diesem Fall , wenn Sie versehentlich passieren undefinedzu action.dataüber Ihre Aktion Schöpfer dann reagieren-addons-Update wird die Rückkehr , undefineddie von diesen Variablen kam. Da dies den gesamten Reduziererstatus überschreibt, wird dies zurückgegebenundefined dass dies den Fehler auslöst.

Debuggen

Sie können dies debuggen, indem Sie die folgenden Stellen untersuchen:

  • Wo der Reduzierer den neuen Zustand erzeugt. Platzieren Sie einen Haltepunkt in diesem Teil, um sicherzustellen, dass Sie nicht empfangen oder zurückkehrenundefined .
  • Wo die Daten an den Aktionsersteller übergeben werden.
totymedli
quelle