Hi i update the initial state in reducer by adding new array variable to it but when i console this reducer state it show me the old state and that array is not in the state which is causing the crash when i try to access that array.
import {
USER_SET_LOC,
USER_SET_FIRST,
} from '../actions/userActions'
const initialState = {
location: {
region: 'ABC',
isDefault: true
},
first: [],
second : []
}
function userReducer (state = initialState, action) {
switch (action.type) {
case USER_SET_LOC: {
return {
...state,
location: {
...action.payload,
isDefault: false
}
}
}
case USER_SET_FIRST: {
return {
...state,
first: action.payload
}
}
default:
return state;
}
}
export default userReducer
i added the second array later.
when i console the state in another class i get this
{"first": [], "location": {"isDefault": true, "region": "ABC"}}
Whatever i change in the initialState it does not appear.
When i uninstall it and reinstall the app then new changes appear
Related
I am working on a application which uses redux for state management. There, at a some condition, I want to update the state.
My initial state and reducer function looks like this:
import { createSlice } from '#reduxjs/toolkit';
const filterDataTemplate = {
programId: '',
year: '',
};
const initialState = {
//some other state
filterData: { ...filterDataTemplate },
};
const slice = createSlice({
name: 'editFilterSlice',
initialState: initialState,
reducers: {
updateFilterProgramId: (state, action) => {
state.filterData.programId = action.payload;
},
updateFilterYear: (state, action) => {
state.filterData.year = action.payload;
},
},
});
export const {
updateFilterYear,
updateFilterProgramId,
} = slice.actions;
export default slice.reducer;
So filter details containg year and programId is obtained with the help of this code:
const filterDetails = useAppSelector(
(state) => state.locationsFilter.filterData
);
Let's say I have filter data initially:
filterDetails: {year:2021, programId: "Ameria"}
And i want to have my new filter data to be
filterDetails: {year: "", programId: "Ameria"}
So for this what I am doing:
const handleDelete = (e) => {
e.preventDefault();
if (//some condition) {
console.log("delete is called");
dispatch(updateFilterYear(''));
} else {
dispatch(updateFilterProgramId(''));
}
}
handleDelete function is getting called properly when I am clicking a button because I am getting value inside console.
But after running this code my filter data is not updating. I am not sure what I am doing wrong.
Please help with this.
Action.payload is of object type. So You should reference action.payload.year.
I hope this example will be of any use
setTodoDate: {
reducer: (state, action: PayloadAction<TodoDate>) => {
state.currentDate = action!.payload.date;
},
prepare: (value) => ({
payload: { ...value },
}),
}
I think the issue is because you are trying to mutate your state directly. This is bad practice, and Redux state (and more generally react) is intended to be immutable. Reducers should return a copy of the state, along with the updated values. Documentation linked below.
Redux Documentation
Try writing your reducers like the following
updateFilterYear: (state, action) => {
return {
...state,
filterData: {
...state.filterData,
year: action.payload
}
},
updateFilterProgramId: (state, action) => {
return {
...state,
filterData: {
...state.filterData,
programId: action.payload
}
},
So this is my current reducer:
import { Reducer } from 'redux';
import {
EventState,
LOAD_EVENTS,
LOAD_EVENT_BY_ID,
FETCH_MORE_EVENTS
} from '../types/eventTypes';
export const initialState = {
eventsList: [],
event: undefined,
isLastPage: false
};
const eventReducers: Reducer<EventState, any> = (
state = initialState,
action
) => {
switch (action.type) {
case LOAD_EVENTS:
return {
...state,
eventsList: action.eventsList
};
case FETCH_MORE_EVENTS:
return {
state,
eventsList: state.eventsList.concat(action.eventsList),
isLastPage: action.eventsList.length === 0
};
default:
return state;
}
};
export default eventReducers;
As you see both cases LOAD_EVENTS and FETCH_MORE_EVENTS share the key eventsList, on fetch more events I am calling state like this state instead of ...state because it seems to re init the state of the whole reducer. But, is that the proper way? I think that if this reducer grows up, that will be a bug.
So what can I do to clean that reducer properly to make? Like all I need is that LOAD_EVENTS fires then eventsList should get clear and fill out again by what LOAD_EVENTS brings. And basically I only need to reset the state of eventsList but rest should remain the same.
when you calling state like state instead of ...state, you aren't re-init the state, but storing the previous state inside the new state, like this example below:
state = {
eventsList: [...someEvents],
event: undefined,
isLastPage: false,
state: {
eventsList: [...someEvents],
event: undefined,
isLastPage: false,
state: {
eventsList: [...someEvents],
event: undefined,
isLastPage: false
}
}
};
This is not a good pattern/practice, only if is super necessary.
So the correct, it's reset the previous state with initialState when fetch more events.
export const initialState = {
eventsList: [],
event: undefined,
isLastPage: false
};
const eventReducers: Reducer<EventState, any> = (
state = initialState,
action
) => {
switch (action.type) {
case LOAD_EVENTS:
return {
...state,
eventsList: action.eventsList
};
case FETCH_MORE_EVENTS:
return {
...initialState,
eventsList: state.eventsList.concat(action.eventsList),
isLastPage: action.eventsList.length === 0
};
default:
return state;
}
};
But how you say, it's only need to reset the state of eventsList but rest should remain the same, you can keep the same to this reducer:
case LOAD_EVENTS:
return {
...state,
eventsList: action.eventsList
};
Because when you set eventsList like the example above, you are reset the eventsList and fill out again with new data. But don't forget the problem about the first example that I say.
Ok so i'll try to sum it up. I've modified my code to have 2 reducers instead of 1 (for better reading) and i started getting an infinite loop.
The redux props seem to keep updating, however there's no change in them.
This is my reducer file.
import { ADD_LISTITEM } from "../constants/action-types.jsx";
import { SET_SPOTOKEN } from '../constants/action-types.jsx';
import { SET_LISTVIEW } from '../constants/action-types.jsx';
import { CHECK_SPOTOKEN } from '../constants/action-types.jsx';
import { SET_FORMPANEL } from '../constants/action-types.jsx';
import { SET_FORMPANELTYPE } from '../constants/action-types.jsx';
import { combineReducers } from 'redux'
const initialState = {
listItems: [],
spoToken: '',
listView: "All Items",
checkToken: "200",
showNotice: true,
//showFormPanel: false,
//formPanelType: ''
};
const listState = {
showFormPanel: false,
formPanelType: ''
}
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_LISTITEM:
return { ...state, listItems: [...state.listItems, action.payload] };
case SET_SPOTOKEN:
return { ...state, spoToken: action.payload };
case SET_LISTVIEW:
return { ...state, listView: action.payload };
case CHECK_SPOTOKEN:
return { ...state, checkToken: action.payload };
default:
return state;
}
};
const listReducer = (state = listState, action) => {
switch (action.type) {
case SET_FORMPANEL:
return { ...state, showFormPanel: action.payload };
case SET_FORMPANELTYPE:
return { ...state, formPanelType: action.payload };
default:
return state;
}
}
const allReducers = combineReducers({
rootReducer,
listReducer
});
export default allReducers;
Any idea why this might cause a problem?
Edited with Redux State ->
{
root: { … }, newItem: { … }, forms: { … }
} forms: {
$form: { … }, newItem: { … }
} newItem: { Title: "", ItemID: "", OfferingID: "", DeliveryModality: "", Status: "", … }
root: listReducer: {
showFormPanel: false, formPanelType: ""
} rootReducer: {
listItems: Array(4), spoToken: { … }, listView: "All Items", checkToken: "200", showNotice: true
}
Puttin the above into words since it looks kinda messed up.
State contains -> forms, newItem, root. Root contains listReducer and rootReducer.
FIXED: only 1 combineReducers allowed.
Apparently since i didnt have different reducers before adding this one i had another combineReducers in my redux-form file. I assume that was the problem (having 2 combineReducers) because it seems to work fine after modifying it.
I am trying to update the state of an item in the store.
This is working but it is not returning the state in the format I am looking for.
The state looks like this:
state : {
watchList: [
{
movie: {
'name' : 'Batman'
}
}
]
}
however, I have been attempting to make my state look like this (i.e. not have another object inside the first object, but just an array of objects).
state : {
watchList: [{'name' : 'Batman'}. {'name': 'Superman'}]
}
My reducer looks like this:
export default (state = [], action) => {
switch(action.type) {
case 'MOVIE_ADDED_TO_LIST':
return [
...state,
{
movie: movie.event
}
];
default:
return state;
}
};
and my action looks like this:
export const addMovieToList = (movie) => {
return {
type: 'MOVIE_ADDED_TO_LIST',
movie
};
};
And here is how I am mapping stateToProps.
function mapStateToProps(state, props) {
return {
WatchListEvents: state.watchList
}
}
export default connect(mapStateToProps)(WatchList);
export default (state = [], action) => {
switch(action.type) {
case 'MOVIE_ADDED_TO_LIST':
return [
...state,
movie.event
];
default:
return state;
}
};
I'm having surprisingly difficult time figuring this out, essentially I'm trying to set state to initial state, so far I tried:
// -- Initial state ------------------------------------------------------------
const INITIAL_STATE = {
search: {
listings: []
},
listings: []
}
// -- Story structure for story editor -----------------------------------------
export default function(state = INITIAL_STATE, action) {
switch(action.type) {
case ACTIONS.RESET_STATE:
return { ...state, INITIAL_STATE }
default:
return state;
}
}
this just adds initial state to existing one
case ACTIONS.RESET_STATE:
return { ...state, state = INITIAL_STATE }
this returns error
case ACTIONS.RESET_STATE:
return { ...state, state: INITIAL_STATE }
this is adding initial state to existing one gain
case ACTIONS.RESET_STATE:
return { ...state, search: { listings:[] }, listings: [] }
This works, but I start getting weird mutation errors.
The proposed solution of Anders is right, but has potential problem with immutables. This generates always new object.
case ACTIONS.RESET_STATE:
return { ...INITIAL_STATE };
Look at Jiri Fornous solution instead, as this will mutate your data.
An even easier way is to just return INITIAL_STATE.
case ACTIONS.RESET_STATE:
return INITIAL_STATE;
If you simply want to reset state completely, just return the value of INITIAL_STATE:
export default function(state = INITIAL_STATE, action) {
switch(action.type) {
case ACTIONS.RESET_STATE:
return {
search: {
listings: []
},
listings: []
};
default:
return state;
}
}
If you want to keep the INITIAL_STATE in a single place. Change the initial state creator to a function:
function get_INITIAL_STATE => {
return { search: {
listings: []
},
listings: []
}
}
export default function(state = get_INITIAL_STATE(), action) {
switch(action.type) {
case ACTIONS.RESET_STATE:
return get_INITIAL_STATE();
default:
return state;
}
}