I create a toy example with NodeJS and redux. I create a store like this:
var initialState = {config: defaultConfig, customData: data};
const store = createStore(reducers, initialState)
I make sure defaultConfig
and data
are defined, i.e. i really stop the debugger before the statement and I can confirm the config is really there. However, inside the reducers, the state is undefined
. Why?!
The reducers are:
const configReducer = (config: any, action: any): any =>{
return config;
}
const customData = (customData: any, action: any): any => {
return customData;
}
const reducers = combineReducers({config: configReducer, customData: customDataReducer})
So I am explicitly giving an initial state, but redux will call the reducers with undefined
.
I know I could put the initial state as default parameter in the reducers, or use any other work around. That is not the question here.
The question is: why this is not working if I pass the initial state when building the store.
When Redux initializes it dispatches a "dummy" action to fill the state. So your counter reducer was called with state equal to undefined . This is exactly the case that "activates" the default argument. Therefore, state is now 0 as per the default state value ( state = 0 ).
You can set it at the reducers . Reducers can also set initialState by looking at the incoming state argument (which would be undefined if createStore is not called with initialState ) and returning the values they would like to use as default.
Reducer "blogTypeVisibilityFilter" 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. If you don't want to set a value for this reducer, you can use null instead of undefined.
In a nutshell: it's Redux the one who passes the initial state to the reducers, you don't need to do anything.
Reducers are functions that take the current state and an action as arguments, and return a new state result. In other words, (state, action) => newState. A Redux app really only has one reducer function: the "root reducer" function that you will pass to createStore later on.
The initial state is zero. Why? Because the second argument to createStore was undefined. This is the state passed to your reducer the first time. When Redux initializes it dispatches a "dummy" action to fill the state. So your counter reducer was called with state equal to undefined.
In ReactJS, redux is the tool for the management of the state throughout the whole application globally. The state that we use inside redux as a store can only be modified with the help of the actions. But, this state must be specified somewhere first to use it. We prefer to declare our initial state at the reducer files.
The state that we use inside redux as a store can only be modified with the help of the actions. But, this state must be specified somewhere first to use it. We prefer to declare our initial state at the reducer files. Then we use that reducer in the store and provide that store use inside the whole application.
This behaviour is specific to combineReducers
:
Any reducer passed to combineReducers must satisfy these rules:
For any action that is not recognized, it must return the state given to it as the first argument.
It must never return undefined. It is too easy to do this by mistake via an early return statement, so combineReducers throws if you do that instead of letting the error manifest itself somewhere else.
If the state given to it is undefined, it must return the initial state for this specific reducer. According to the previous rule, the initial state must not be undefined either. It is handy to specify it with ES6 optional arguments syntax, but you can also explicitly check the first argument for being undefined.
With combineReducers
, a reducer is initially called with undefined
to assert that it doesn't break these rules. Only then it will be called with initialState
.
So initialState
is intended to hydrate the state with initial values, not provide default values.
A reducer should be able to handle unexpected state on unexpected action (default
clause in switch
), in this case initial value should be forcedto be anything but undefined
, e.g.:
const configReducer = (config: any = null, action: any): any =>{
return config;
}
const customData = (customData: any = null, action: any): any => {
return customData;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With