I am trying to call the current state of my application in the redux reducer. In the first case, I fire my action off with this code–
store.dispatch(addNotificationID(item, 'start', notificationID));
console.log(item.id, 'start', notificationID);
console.log(store.getState().StorageReducer.IDs); //shows accurate state
This successfully fires the first case of my redux reducer and then the console log of console.log(store.getState().StorageReducer.IDs) shows the newly added ID in the state. Then, I have the second case that I am going to eventually use to remove the ID object from the state. If I reload the application and then this action fires of type CANCEL_NOTIFICATION it will log the correct store. But if I fire that action in the same session that an ID was added, that will not log to the store.
here is my reducer:
const initialState = {
IDs: []
};
const storage = (state = initialState, action) => {
switch (action.type) {
case ADD_NOTIFICATION_ID:
return {
...state,
IDs:
[
...state.IDs,
{
itemID: action.item.id,
reminderType: action.reminderType,
notificationID: action.notificationID
}
]
};
case CANCEL_NOTIFICATION:
console.log(state.IDs);
return { ...state };
For example, say IDs was this when the application booted up:
Array [
Object {
"itemID": "_UE9dINF",
"notificationID": "F7AFB9F9-45C3-4A90-AD70-1A2334CE282A",
"reminderType": "start",
},
]
Then my addNotifications action fires. During that session when the console.log(store.getState().StorageReducer.IDs) gets called it would look like this:
Array [
Object {
"itemID": "_UE9dINF",
"notificationID": "F7AFB9F9-45C3-4A90-AD70-1A2334CE282A",
"reminderType": "start",
},
Object { //this is the new object that was just added to state
"itemID": "z4ACCSZR",
"notificationID": "3850E623-CFA7-483C-BF22-DB07C746CE37",
"reminderType": "start",
},
]
If I reload the session and then fire the cancelNotification action the reducer case will also log that ouptut. However, if I run that action in the same session I fired the addNotifications action, instead of looking like above it will only look like this:
Array [
Object {
"itemID": "_UE9dINF",
"notificationID": "F7AFB9F9-45C3-4A90-AD70-1A2334CE282A",
"reminderType": "start",
},
]
Then if I reload the session it will have the two objects as it is supposed to. I really don't understand why or how this is possible. I am using redux-persist and think the bug may be coming somewhere from that.
My redux store:
const todoPersistConfig = {
key: 'TodoReducer',
storage: AsyncStorage,
whitelist: ['todos'],
stateReconciler: autoMergeLevel2
};
const storageConfig = {
key: 'StorageReducer',
storage: AsyncStorage,
whitelist: ['IDs'],
stateReconciler: autoMergeLevel2
};
const remindersPersistConfig = {
key: 'remindersreducer',
storage: AsyncStorage,
whitelist: ['notificationIDs'],
stateReconciler: autoMergeLevel2
};
const reducers = combineReducers({
ModalReducer,
RemindersReducer: persistReducer(remindersPersistConfig, RemindersReducer),
StorageReducer: persistReducer(storageConfig, StorageReducer),
TodoReducer: persistReducer(todoPersistConfig, TodoReducer),
AuthReducer
});
export default function storeConfiguration() {
const store = createStore(
reducers,
{},
composeEnhancers(
applyMiddleware(ReduxThunk)
)
);
const persistor = persistStore(store);
return { persistor, store };
}
There are no problems with the todo reducer, but I recreated an extra reminders reducer to duplicate and see if the issue persisted and it had the same results.
Related
I have a problem with my code. I currently have some data like the one below;
users: [
{
name: 'bolu',
features: ['Tall'],
},
{
name: 'cam',
features: ['Bearded', 'Short'],
},
],
};
What I am trying to do is delete/remove a single feature - for example if I pass in 'short' into my redux action. I'd like for it (the 'Short' text) to be removed from the features array. I currently have my redux action set up this way:
export interface UsersDataState {
name: string,
features: Array<string>,
}
export interface UsersState {
users: UsersDataState[];
}
const initialState: UsersState = {
users: [],
};
export const usersSlice = createSlice({
name: 'users',
initialState,
reducers: {
removeUser: (state, action: PayloadAction<string>) => {
const removedUsers = state.users.filter((user) => user.features.indexOf(action.payload));
state.users = removedUsers;
},
},
});
So here I am passing in the value in (action.payload is the value being passed in). When this action is dispatched, I want to remove just the word that is passed in from the features array. I hope this is clearer now.
This doesn't work for some reason and I am unable to figure out why. Any help would be appreciated please, thank you.
Your code doesn't match your state structure. Replace traits with users, and values with features.
It looks like that's a part of a reducer, not an action (which is an object, not a function).
You should be returning a new state from the reducer.
Given your update the function should be called removeFeature.
So, I've corrected a few bits of your code based on what I remember from Redux. Note: contrived example.
// State
const state={users:[{name:"joe",features:["Mean","Short"]},{name:"bolu",features:["Tall"]},{name:"cam",features:["Bearded","Short"]}]};
// The reducer accepts a state, and an action
function reducer(state, action) {
// We destructure the type, and payload, from the action object
const { type, payload } = action;
// Depending on the type...
switch (type) {
case 'removeFeature': {
// `map` over the users (we return a new state array)
return state.users.map(user => {
// `filter` out the feature elements
// that match the payload
const updatedFeatures = user.features.filter(feature => {
return feature !== payload;
});
// Return a new updated object
return { ...user, features: updatedFeatures };
});
}
default: return state;
}
}
const updatedState = reducer(state, {
type: 'removeFeature',
payload: 'Short'
});
console.log(updatedState);
I'm using react-redux to fetch data from MongoDB database and put it into React App.
I've following structure to work upon:
const initialState = {
Level: [{
wId: Math.random(),
Level1: [
{
id: Math.random(),
item1: 'item1',
item2: 'item2'
},
.......
],
Level2: [
{
id: Math.random(),
item1: 'item1',
item2: 'item2'
},
.......
]
}]
}
Redux Function:
export default function (state = initialState, action) {
switch (action.type) {
case GET_ITEMS:
return {
...state,
// what should i write here to get above mentioned state structure
}
..............
}
Note:
Initially Level is empty. So if new data is received in payload then the following structure should be formed.
How to update particular item like item1 at level2
Sample Input:
action.payload = {
id1: 23234, // should be assigned in Wid
ITEM: [ // Level1
{
id2: 89724, // should be assigned in Level1.id
i: 'abc', // should be assigned in Level1.item1
j: 'xyz' // should be assigned in Level1.item2
}
]
}
I you dont know how many items you are going to get its would be difficult. One way to work around this issue could compare the previos state with current state and update only necessary part that got changed.
You can use number of libraries or follow any answer in How to determine equality for two JavaScript objects? to compare the objects.
Ideally you would need different actions to update Level, Level ->Level 1 and so on.
Create separate actions for adding levels. Call that action when on user events which add a level to your initial state.
export default function (state = initialState, action) {
switch (action.type) {
case GET_ITEMS:
return {
...state,
// what should i write here to get above mentioned state structure
}
case ADD_LEVELS:
return {
...state,
Level: [...state.Level, action.payload.Level]
}
}
You can move the id generation logic to the component as it will make your life simpler.
I want to update a state value where the key is also dynamic in redux reducer, but I tried various method I am not able to do it.
So my Initial State looks like this
const initalState = {
ingredients:{
bacon:0,
salad:0,
cheese:0,
meat:0,
},
price:100
};
And my data passed in reducer looks like this
{type: "INCREASEQUANTITY", payload: {
item: "bacon",
value: 1
}}
And I want result state looks like
const initalState = {
ingredients:{
bacon:1, //updated value
salad:0,
cheese:0,
meat:0,
},
price:100
};
I tried to update it immutably, but its showing syntax error.
My code for this looks like this
return{
...state,
state.ingredients:{
...state.ingredients,
action.payload.item:state.ingredients[action.payload.item]+action.payload.value //this line explained below
}
}
I want to update it with key and value both dynamically like bacon:0+1 but getting syntax issue.
Please help.Thank you
You were on the right track
return {
...state,
ingridients: {
...state.ingridients,
[action.payload.item]: state.ingredients[action.payload.item] + action.payload.value
}
}
After the user clicks a button I want to replace the data in my vuex store with the data from my local object. But I am stuck at the mutation. Here is some code for more details.
This is the method that is called after the user clicks a button.
(this.tableview is a local object with the same values as the vuex object)
updateVuexTableview() {
// eslint-disable-next-line no-console
console.log("Update Vuex Table view");
this.updateTableview(this.tableview)
}
In this method my vuex action is called. Which looks like this:
(updTableview is the new data that I want to insert)
async updateTableview({commit}, updTableview) {
const response = await axios.put(
`http://localhost:3000/tableview`,
updTableview
);
// eslint-disable-next-line no-console
console.log(response.data);
commit('updateTableviewMut', response.data);
},
This is the mutation that is called. That's where I am stuck. I have tried to pop the data and push it again, but nothing works so far.
updateTableviewMut: (state, updTableview) => {
state.tableview.push(updTableview)
},
This right here is my state:
const state = {
tableview: {
"thema_c": true,
"status_c": true,
"priority_c": true,
"custom1_c": true,
"custom2_c": true,
"customFieldName1": "Custom1",
"customFieldName2": "Custom2"
},
};
You cannot update an object by .push you have to reassign object or set a specific field.
Just change your code like this:
If you want to add a key value pair you can do:
updateTableviewMut: (state, updTableview) => {
state.tableview['keyname'] = updTableview
}
Or if you want to reassign an object you can do:
updateTableviewMut: (state, updTableview) => {
state.tableview = updTableview
}
Imagine a UI with two React components:
<FilterContainer />
<UserListContainer />
We pull down an array of users from the server:
[
{
name: 'John',
enjoys: ['Sailing', 'Running']
},
{
name: 'Bob',
enjoys: ['Running', 'Eating']
},
{
name: 'Frank',
enjoys: ['Sailing', 'Eating']
}
]
The UI looks a little like this:
Filter: Sailing Running Eating
UserList:
John
Frank
You can click on either a filter or a user. To get to this stage, we've clicked on 'Sailing' and then on 'Frank' (maybe we see a nice photo of Frank in the middle of the screen).
My Redux state, built using combineReducers, looks like this:
{
ui: {
filter: {enjoys: 'Sailing'},
userList: {selected: 'John'}
}
data: [user array]
}
I have two actions, SELECT_USER and SELECT_FILTER.
When I click on a filter (SELECT_FILTER fires), I want the ui.userList.selected to persist if that user is still in the filter, and the ui.userList.selected to be set to null if the user is not in the filter.
So if I now click on Eating, I'll see a list with Bob and Frank in it, and Frank is selected. But if I click on Running, I'll see John and Bob, but neither are selected.
However I'm struggling to do this in the conventional Redux methodology. When the userList reducer sees the SELECT_FILTER action, there's no way for it to check the data state to see if the currently selected user is still in that filter condition or not.
What's the right way to do this?
function filter(state = {enjoys: null}, action) {
switch (action.type) {
case SELECT_FILTER:
return {
...state,
enjoys: action.enjoys
}
default:
return state
}
}
function userList(state = {selected: null}, action) {
switch (action.type) {
case SELECT_USER:
return {
...state,
selected: action.name
}
default:
return state
}
}
const ui = combineReducers({
filter,
userList
})
let initialUsers = [
{
name: 'John',
enjoys: ['Sailing', 'Running']
},
{
name: 'Bob',
enjoys: ['Running', 'Eating']
},
{
name: 'Frank',
enjoys: ['Sailing', 'Eating']
}
]
const rootReducer = combineReducers({
ui,
data: (state=initialUsers) => state // in reality, loaded from server
})
export default rootReducer
Reducer should be aware only of a small part of state.
Good place for described logic is the action creator. With redux-thunk you will be able to make a decision based on a global state.
function selectFilter(enjoys) {
return (dispatch, getState) => {
dispatch({type: SELECT_FILTER, enjoys});
// if getState().ui.userList.selected exists in getState().data
dispatch({type: SELECT_USER, name: null});
}
};
You need another action for this.
If you are filtering the users in the data reducer, you will need to dispatch an action in one of your components' hooks (componentWillUpdate or componentWillReceiveProps) when you detect that the array of users has changed. This action will provide your filter reducer with the current array of users, and there you can set the selected field as you like.
If you are filtering the users in the server, I guess you already have an action like FETCH_USERS_SUCCESS that you can use for this.
It should be handled by the filter reducer. You need to send the users data as part of the action payload. Hence the reducer could Calc the selected user logic. You should consider adding a new action, as #cuttals suggested.