Reducer always returning initial state - reactjs

I have been playing with react-native and redux and I have encountered an error. The state is always initialised as the starting one although the payload is present when I debug it inside the reducer.
This is my reducer file
let initialState = {
filterList: [],
isFetching: false,
activeFilters: [],
}
export function fetchFilterList(state = initialState, action) {
return { ...state, isFetching: true };
}
export function fetchFilterListSuccess(state, action) {
return {
...state,
filterList: action.payload,
isFetching: true,
dsad: "dada",
}
}
export function fetchFilterListError(state, action) {
return { ...state, isFetching: false };
}
This is where I combine them into one function(in the same file as above):
export function combinedFiltersReducers(state = initialState, action) {
switch (action.type) {
case ACTION_TYPES.FETCH_FILTER_LIST:
return fetchFilterList(state, action);
case ACTION_TYPES.FETCH_FILTER_LIST_SUCCESS:
return fetchFilterListSuccess(state, action);
case ACTION_TYPES.TOGGLE_FILTER_ITEM:
return toggleFilterItemStart(state, action);
case ACTION_TYPES.TOGGLE_FILTER_ITEM_SUCCESS:
return toggleFilterItemSuccess(state, action);
default:
return state;
}
}
This is my combine reducers function in a separate file called main reducers.
export default combineReducers({
adList: fetchAdListSuccess,
filterList: combinedFiltersReducers,
});
Here is the where I receive the state in the component, and it always falls to the initial state.
const mapStateToProps = state => ({
filterList: state.filterList,
});
const mapPropsToDispatch = dispatch => ({
fetchFilterList:() => dispatch(fetchFilterList()),
toggleFilterItem: (data) => dispatch(toggleFilterItem(data)),
});
export default connect(
mapStateToProps,
mapPropsToDispatch
)(FilterComponent);
I can not find the error, so I need a bit of help. Thanks in advance.

Okay, I have found an issue. Taking an example:
return fetchFilterList(state, action);
You're calling fetchFilterList method and passing initial state. So every time you call it actually passes the initial state. And that method is just copying the initial state. Rather do like this:
return fetchFilterList(...state, action);

I am going to assume that your ACTION_TYPES do not have the appropriate type(s) for the function you are calling so it is reverting to the default case.

Related

React-Redux add new value to the state

I am having a two step form in react. The first step of the form we ask some information to the user and then I add it to the state. The second step of the form I ask some more information to the user and add it to the state, so instead of appending the information that was asked on step 2 of the form, it overrides the state, so the state now only the info that was asked in the step 2 of the form. How can I add the have both the information together. When i try to ...state it gives me error as state is not iterable.
const infoReducer = (state = {}, action) => {
switch(action.type) {
case 'STEP_1':
return action.payload
case 'STEP_2':
return action.payload
default:
return state
}
}
check this out
import { ActionTypes } from "../../Constant/ActionType";
const initState = {
Auth: {},
};
const AuthReducer = (state = initState, action) => {
switch (action.type) {
case ActionTypes.AuthUser:
return {
...state,
Auth: action.payload,
};
default:
return state;
}
};
export default AuthReducer;
first, copy the current state data and add a new action payload as second parameter
case 'ADD_Data':
return {...state,action.payload}

react The slice reducer for key "mods" returned undefined during initialization. (redux)

reducers/index.js
import { combineReducers } from "redux";
import mods from "./mods.js";
export default combineReducers({ // <----- error comes from here
mods
})
reducers/mods.js
import { GET_MODS } from "../actions/types"
const initialState = {
mods: [],
}
export default function(state = initialState, action) {
switch(action.type) {
case GET_MODS:
return {
...state,
mods: action.payload
}
}
}
No idea why this is happening, I have done similiar things to this but have never encountered this problem, I am new to redux so its probably a stupid mistake...
// The error
Error: The slice reducer for key "mods" 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.
Try with adding the default case,
export default function (state = initialState, action) {
switch (action.type) {
case GET_MODS:
return {
...state,
mods: action.payload
}
default:
return state
}
}
You probably forgot to include a default case. It could be something like this:
import { GET_MODS } from "../actions/types"
const initialState = {
mods: [],
}
export default function(state = initialState, action) {
switch(action.type) {
case GET_MODS:
return {
...state,
mods: action.payload
}
default:
return state
}
}
}
I had this same issue, and the problem turned out to be that action.payload was undefined...
So I just had to make sure when calling the action that the payload was not undefined.

Two Boolean states in the Init State of Reducer, One returns undefined

I've been working with redux for the last couple weeks and was incorporating it into my projects when I ran into this wall. Pretty common reducer for modals being rendered so i can animate them before unmounting them.
const initialState = {
isModalOpen: false,
test: false
}
export default function(state = initialState, action) {
switch (action.type) {
case "modalInteraction":
return {
isModalOpen: action.payload
};
case "testModalInteraction":
return {
test: action.payload
};
default:
return state;
};
}
Sadly, the test property is still returning as undefined despite the fact that the other initial state in the same reducer can be called without a problem. I even removed all the testModalInteraction dispatches in the case that that somehow upset the datatype. I just can't spot the difference that keeps returning undefined.
When you return the new state, make sure to spread the initial state (...state) and then change whatever values you need to change.
const initialState = {
isModalOpen: false,
test: false
}
export default function(state = initialState, action) {
switch (action.type) {
case "modalInteraction":
return {
...state,
isModalOpen: action.payload
};
case "testModalInteraction":
return {
...state,
test: action.payload
};
default:
return state;
};
}
If it is still undefined, make sure the payloads are defined for both actions.
For example, your modalInteraction action could look like
export const modalInteraction = (bool) => ({
type: "modalInteraction",
payload: bool
})
P.S., you can destructure the action object. This allows you to use "type" instead of "action.type" and "payload" instead of "action.payload".
const initialState = {
isModalOpen: false,
test: false
}
export default function(state = initialState, action) {
const {type, payload} = action;
switch (type) {
case "modalInteraction":
return {
...state,
isModalOpen: payload
};
case "testModalInteraction":
return {
...state,
test: payload
};
default:
return state;
};
}

Setting the initial state of redux in reducers

Hey guys i am stuck in a situation in which i have to set the inital state of reducer to some value let me show you the code
First of all i have an action creater like this
export const fetchuser = () => {
return async dispatch => {
const res = await axios.get("/api/currentuser");
dispatch({
type: "fetchuser",
payload: res.data
});
};
};
which just fetches the data from api and dispatches an action to reducer
export default function(state = {}, action) {
switch (action.type) {
case "fetchuser":
return action.payload||false;
default:
return state;
}
}
now in second action creater i have to make a post request and increase the "credits" value in user database
export const handletoken = token => {
return async dispatch => {
const res = await axios.post("/api/stripe", token);
dispatch({ type: "credits", payload: res.data });
};
};
so i get the updated value here then i pass this on to the reducer
export default function(state = {}, action) {
switch (action.type) {
case "credits":
return action.payload
default:
return state;
}
}
and then combine them in reducer/index.js
export default combineReducers({
auth: authreducer,
credits:creditsreducer
});
console log of auth reducer in app.js in mapstatetoprops function gives
auth:
credits: 40
googleid: "109463598810933991924"
__v: 0
_id: "5d7fff2c4cb0604139055ce4"
so in credits reducer as u can see i have defined initial value of state as an empty object but i want to set it as the value of credits key of auth reducer, I could easily set it to array or an object hardcoding it but here i need to set its value as a value which is already in my another reducer so how can i achieve this ?
Assuming you need to wait for "fetchuser" to succeed to set credits in your creditsreducer you can handle the "fetchuser" action in your creditsreducer as well:
export default function(state = {}, action) {
switch (action.type) {
case "fetchuser":
return action.payload ? action.payload.credits : state;
case "credits":
return action.payload
default:
return state;
}
}
Always keep previous reducer state value. Otherwise no use of redux state value. like this
1.export default function(state = {}, action) {
switch (action.type) {
case "fetchuser":
let data = action.payload||false;
return {
...state,
fetchuser: data //any where you can access fetchuser data as well as previous state will not change.
}
default:
return state;
}
}
Change all the reducers like above.

Passing redux state to reducer

I'm trying to delete an element from dom by clicking on it. I did it without the problem without redux thunk but now I have a problem. My reducer doesn't know about the state. How do let him know what items are?
Action:
export function deleteItem(index) {
return {
type: 'DELETE_ITEM',
index
};
}
My reducer that shows undefined.
export function deleteItem(state = [], action) {
switch (action.type) {
case 'DELETE_ITEM':
const copy = state.items.slice()
console.log(copy)
default:
return state;
}
}
Heres my actual code https://github.com/KamilStaszewski/flashcards/tree/develop/src
I saw your code and you are defining a new reducer for each of the operations you want to get done to your items (e.i itemsHaveError, deleteItem, ...) but the correct way of doing this is to store all of the relevant functions for the items to a single reducer which holds the data needed to change whenever some action to the items happens, but in the way you did it, any time any action happens because your reducers are separated the initial state gets empty as you have passed to the functions and the reducers do not know about their related data so they overwrite them with the empty initial state, the correct way would be like this to write a single reducer for items:
const initialState = {
isLoading: false,
hasError: false,
items: [],
};
export default function(state = initialState, action) {
switch (action.type) {
case ITEMS_HAVE_ERROR:
return {
...state,
hasError: action.hasError,
};
case ITEMS_ARE_LOADING:
return {
...state,
isLoading: action.isLoading,
};
case ITEMS_FETCH_DATA_SUCCESS:
return {
...state,
items: action.items,
};
case DELETE_ITEM:
const copy = state.items.slice()
return {
...state,
items: copy,
};
default:
return state;
}
}
so this would be your item.js and your item reducer and the only one that should get to combineReducer function.
Indicate the initial State of the reducer by default , the state is an empty array and you can't access the state.items , cause it is undefined. Assume this:
const x = [];
x.foo.slice();
that would return an error . Thus from :
state = []
change it to :
state = {
items:[]
}
applying it to your code:
export function deleteItem(
state = {
items:[]
},
action) {
switch (action.type) {
case 'DELETE_ITEM':
const copy = state.items.slice()
console.log(copy)
default:
return state;
}
}

Resources