Why does my Redux reducer think my state is undefined? - redux

I believe I'm copying the Todo tutorial pretty much line for line, I am getting this error:
Error: Reducer "addReport" 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.
And here is my addReport reducer:
const addReport = (state = [], action) =>
{
console.log(state)
switch (action.type) {
case ADD_NEW_REPORT:
return [...state,
addReports(undefined, action)
]
}
}
I added the logging statement and can verify that it returns an empty array. Even setting state to something like 1 will produce the same results. What am I missing?

You are missing the default of the switch case.
default: {
return {
...state
}
}
Redux won't play along like a nice kid if you forget to do it!
Or alternatively, you can explicitly return at the end the initial state:
If the state passed to the reducer is undefined, you must explicitly return the initial state.

Related

getting Error "The slice reducer for key "weathReducer" returned undefined during intialization in my expo app"

running into Error: The slice reducer for key "weatherReducer" 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.
at node_modules/react-native/Libraries/Core/ExceptionsManager.js:104:6 in reportException
at node_modules/react-native/Libraries/Core/ExceptionsManager.js:172:19 in handleException
at node_modules/react-native/Libraries/Core/setUpErrorHandling.js:24:6 in handleError
at node_modules/#react-native/polyfills/error-guard.js:49:36 in ErrorUtils.reportFatalError
at node_modules/metro-runtime/src/polyfills/require.js:204:6 in guardedLoadModule
at http://192.168.0.12:19000/node_modules/expo/AppEntry.bundle?platform=android&dev=true&hot=false&strict=false&minify=false:146332:3 in global code
In general when writing a reducer function, there is an initialization call which invokes the reducer without an action argument. If you're in that call, you must return initialState.
var initialState = {
temperature: undefined,
units: 'celsius',
isLoading: false,
}
function weatherReducer(state, action) {
switch(action) {
case 'SET_TEMPERATURE':
return { ...state, temperature: action.temperature }
// ...
default:
return state || initialState // <- here
}
}

stateProps value null in mergeProps

I have a mapStateToProps function that fires off an async request to fetch user data from the server. The successful result of that, fires another action which actually sets the user to the state. All of this works fine. What I need to do now is, after the user data is set on the state, I need to fire another action (setEditUserModel) using the user value that was set from fetchUser.
Since state is not accessible in mapStateToProps, I've added the mergeProps connect option. It's my understanding that this combines ("merges") the mapStateToProps and mapDispatchToProps together. When I do this with the code below, stateProps.user in mergeProps is null even though the actual value when I inspect my AppState is not null.
function mapStateToProps(state: AppState): EditUserStateProps {
return {
user: state.user.data
};
}
function mapDispatchToProps(dispatch: Dispatch<ActionTypes>): EditUserDispatchProps {
return {
fetchUser: (id: number) => {
return fetchUser(id)(dispatch);
},
setEditUserModel: (user: User) => {
dispatch(actionCreators.setEditUserModel(user));
}
}
};
function mergeProps(stateProps: EditUserStateProps, dispatchProps: EditUserDispatchProps, ownProps: EditUserContainerProps) {
return {
...ownProps,
...stateProps,
...dispatchProps,
fetchUser: (id: number) => {
return dispatchProps.fetchUser(id).then(() => {
dispatchProps.setEditUserModel(stateProps.user);
});
}
}
}
Why is my stateProps.user value null?
stateProps.user is null because of how javascript handles a closure's scope chain. When your closure that calls dispatchProps.setEditUserModel() gets executed after the stateProps.fetchUser() Promise is resolved, it is actually referencing the stateProps object that was within scope during its declaration, which happens to still contain a null user prop.
If you were to mutate the Redux store's state instead of copying it and then somehow pass that reference all the way down to your mergeProps(), you might see this bug disappear because now the stateProps in the mergeProps() closure references the same object that is in the redux store. However, this is not advisable because Redux reducers should be pure functions.
Instead, you might consider resolving the Promise returned in stateProps.fetchUser() with the user object and pass that to dispatchProps.setEditUserModel().

Strange type empty Flow syntax inside default statement of Redux reducer

I found the code sample while searching for ways to use flow with redux here:
https://flow.org/en/docs/frameworks/redux/
Peculiar syntax is (action: empty); Is it just a bit of flow magic intended to be used just inside default case of switch statement or does it have other uses?
It looks like out of place function type statement without return value type but with parameter of strange type 'empty', which I couldn't find documentation about.
// #flow
type State = { +value: boolean };
type FooAction = { type: "FOO", foo: boolean };
type BarAction = { type: "BAR", bar: boolean };
type Action = FooAction | BarAction;
function reducer(state: State, action: Action): State {
switch (action.type) {
case "FOO": return { ...state, value: action.foo };
case "BAR": return { ...state, value: action.bar };
default:
(action: empty);
return state;
}
}
empty is Flow's bottom type. I believe the main motivation for its initial introduction was symmetry but it has proven to have some uses. As you have identified it can be used in this case to make Flow enforce exhaustiveness. It can be used similarly in a chain of if/else statements.
However, it can be used anytime when you want Flow to prevent any actual value from ending up somewhere. This is very vague, so here are a couple examples:
// Error: empty is incompatble with implicitly-returned undefined
function foo(): empty {
}
// No error since the function return is not reached
function foo2(): empty {
throw new Error('');
}
function bar(x: empty): void {
}
// Error: too few arguments
bar();
// Error: undefined is incompatible with empty
bar(undefined);
In the foo examples, we can see that Flow enforces that a return is never reached in a function returning empty. In the bar example, we can see that Flow prevents the function from being called.

Redux will execute all subscription callbacks every time an action is dispatched?

Gee, I feel foolish about this, but I have read every part of: http://redux.js.org/ (done the egghead tutorials, and read 4 times the FAQ at: http://redux.js.org/docs/faq/ImmutableData.html
What I did was stub one of my reducers, to always return state, and that is the only reducer being called (checked with breakpoints). Even so, my subscribe event is being called every time the reducer returns state. What Do I not understand? (Action.SetServerStats is being called at a 1Hz rate, and the subscribe is also being called at a 1Hz Rate
BTW the Chrome Redux Extension says thats states are equal, and the React Extension for Chrome with Trace React Updates, is not showing any updates.
I will be glad to remove the question, when someone clues me in. But right now, what I see each each of the reducers being called at 1Hz, and all of them returning the slice of the store that they got (state).
So do I not understand subscribe, and that it returns every time even when the store tree does not get modified (and it is up to react-redux to do shallow compare to figure out what changed if any?)
create store & subscribe
let store = createStore(reducer, initialState, composeWithDevTools(applyMiddleware(thunk)))
store.subscribe(() => console.log("current store: ", JSON.stringify(store.getState(), null, 4)))
reducers.js
import A from './actionTypes'
import { combineReducers } from 'redux'
export const GLVersion = (state = '', action) => {
switch (action.type) {
case A.SetGLVersion:
return action.payload
default:
return state
}
}
export const ServerConfig = (state = {}, action) => {
switch (action.type) {
case A.SetServerConfig: {
let { ServerPort, UserID, PortNumber, WWWUrl, SourcePath, FMEPath } = action.payload
let p = { ServerPort, UserID, PortNumber, WWWUrl, SourcePath, FMEPath }
return p
}
default:
return state
}
}
export const ServerStats = (state = {}, action) => {
switch (action.type) {
case A.SetServerStats:
return state
// let { WatsonInstalled, WatsonRunning, FMERunning, JobsDirSize } = action.payload
// let s = { WatsonInstalled, WatsonRunning, FMERunning, JobsDirSize }
// return s
default:
return state
}
}
export default combineReducers({ GLVersion, ServerConfig, ServerStats })
Correct. Redux will execute all subscription callbacks every time an action is dispatched, even if the state is not updated in any way. It is up to the subscription callbacks to then do something meaningful, such as calling getState() and checking to see if some specific part of the state has changed.
React-Redux is an example of that. Each instance of a connected component class is a separate subscriber to the store. Every time an action is dispatched, all of the wrapper components generated by connect will first check to see if the root state value has changed, and if so, run the mapStateToProps functions they were given to see if the output of mapState has changed at all. If that mapState output changes, then the wrapper component will re-render your "real" component.
You might want to read my blog post Practical Redux, Part 6: Connected Lists, Forms, and Performance, which discusses several important aspects related to Redux performance. My new post Idiomatic Redux: The Tao of Redux, Part 1 - Implementation and Intent also goes into detail on how several parts of Redux actually work.

seamless-immutable is not updating state consistently

I'm working with seamless-immutable and redux, and I'm getting a strange error when updating the state. Here's my code, without the bits like the action return or combineReducers. Just the junk that's running/causing the error.
Initial State
{
things: {
fetching: false,
rows: []
}
}
Action Handler
export default {
[DEALERS_REQUEST]: (state, action) => {
return Immutable({ ...state, fetching: true });
},
[DEALERS_RECEIVE]: (state, action) => {
return Immutable({ ...state, rows: action.payload, fetching: false });
},
Middleware with thunk
export const thingsFetch = (data) => {
return (dispatch, getState) => {
dispatch(thingsRequest());
dispatch(thingsReceive(data));
}
}
Now, what's weird is, if I run these two actions together, everything is fine.
If I only dispatch thingsRequest(), I get a "cannot push to immutable object" error.
I've tried using methods like set, update, replace, merge, but they usually return with "this.merge is not a function".
Am I doing something wrong procedurally or should I contact the module dev to report an issue?
This issue on this was that, on the case of an empty array, the component was trying to write back to the Immutable object with it's own error message.
To get around this, I pass the prop as mutable. There's also some redux-immutable modules that replace the traditional connect function to all the app to pass mutable props to components while maintaining immutability in the state.

Resources