update array inside object inside another array - reactjs

this is my reducer
let initialState = [
{ name: 'john', messages: [{ message: "hi" }] },
{ name: 'max', messages: [{ message: "howdy" }] },
{ name: 'alex', messages: [{ message: "hello" }] },
...
];
const Messages = (state = [], action) => {
switch (action.type) {
case "MESSAGES":
return [...state, ...action.payload];
case "UPDATE_ALEX_MESSAGES":
// HOW TO UPDATE????
default:
return state;
}
};
export default Messages;
how to update alex's messages and push another object to it? (commented part of the above code)
Note:
alex's position is not stable and it may change. so something like state[2] is not what I'm looking for. Something more like state.find(x => x.name === 'alex') is more like it..

The map() method and the spread syntax can help you achieve the desired result.
Using the map() method, iterate over the state array and inside the callback function, check if the current user object's name is "alex", if it is, update the current object's messages property by assigning a new array to it and using the spread syntax to copy the existing messages and then also add the new message in the array.
case "UPDATE_ALEX_MESSAGES":
return state.map((user) => {
if (name === "alex") {
user.messages = [...user.messages, newMessage];
}
return user;
});
You could also avoid mutating the existing object by returning a new object inside the if block.
case "UPDATE_ALEX_MESSAGES":
return state.map((user) => {
if (name === "alex") {
return { ...user, messages: [...user.messages, newMessage] };
}
return user;
});
Ideally, you would pass the name of the user in the payload of the action, so the following condition
if (name === "alex") { .. }
will become
if (name === action.payload) { .. }

I have used this npm package to solve this problem https://www.npmjs.com/package/immer. It is a super useful tool that helps reduce boilerplate code

Related

Redux state return empty state alternatively

I am getting filteredStudents state empty array alternatively even when clicking on a section, it returns an array of students but when I click on a different section, it returns an empty array. When I click again on the same section its value is updated. Is there any fix for this?
let filteredStudents=state.?filteredStudentsReducer?.filteredStudents
// dispatching action from main js value: {section: "A" } students:[{name:"a", division:"A" }, ] I am passing values similar to these.
dispatch({
type: "FILTER_STUDENTS_SECTION",
value: obj,
students: students,
});
// this is the reducer , to filter students based on section
case "FILTER_STUDENTS_SECTION":
//filter students based on section
let obj1 = action.value;
let stds1;
if (state.filteredStudents.length) {
stds1 = state.filteredStudents.filter((s) => {
return s.division === obj1.section;
});
} else {
stds1 = action?.students;
stds1 = stds1.filter((s) => {
return s.division === obj1.section;
});
}
return {
...state,
filteredStudents: stds1,
message: "Students filtered section wise",
loading: false,
};

how to delete nested object in React with spread function

I am working on a simple cart function with react-redux, and I have an object that is structured as below:
{
0: { // product ID
"S" : 1, //product variant and item count
"M" : 1
},
1: {
"L":1
"XL": 5
},
}
I wanted to remove the property based on user action but I was not able to achieve that so far.
Attempt 1: delete function will remove everything within the state instead of removing the selected property.
case REMOVE_PRODUCT_FROM_CART:
let newObject = Object.assign({}, state)
return delete newObject[productId][varient];
Attempt 2: only managed to set the property to null but still not able to remove the item.
case REMOVE_PRODUCT_FROM_CART:
return {...state,
[productId]: {
// check if property exists
...(state?.[productId] ?? {}),
[varient]: null
}
Is there any way to remove the desired property with the spread operator?
Here is one way to do it:
case REMOVE_PRODUCT_FROM_CART:
const newObject = {...state[productId]}
delete newObject[varient]
return {...state, [productId]:newObject}
Here is another way to do it:
const state = {
22: {
ok: 88,
remove: 22,
},
};
const productId = 22;
const varient = 'remove';
const { [varient]: ignore, ...product } = state[productId];
console.log('new state', {
...state,
[productId]: product,
});
Here is another option using Object.entries and Object.fromEntries
const state = {
pId: {
k1: 'v1',
k2: 'v2',
k3: 'v3',
},
};
const reducer = (productId, variant) => {
return {
...state,
[productId]: Object.fromEntries(
Object
.entries(state[productId])
.filter(([key, val]) => key !== variant)
),
}
}
console.log(reducer('pId', 'k2'))

Add to list of an object with variable key in Redux

Intended for this reduce function to add new message into a list of messages with a variable key userId.
let state = {
data: {
user1: ["message1", "message2"],
user2: ["message3", "message4"],
},
};
This is what I've got so far:
case types.MESSAGE_FETCHED:
return {
...state,
data: {
...state.data,
[payload.userId]: [...state.data[payload.userId], payload.message],
},
};
The error I'm getting this error:
TypeError: state.data[payload.userId] is not iterable
I found that, I have to initialize an empty array first.
case types.MESSAGE_FETCHED:
if (!(payload.userId in state.data)) {
state.data[payload.userId] = [];
}
return {
...state,
data: {
...state.data,
[payload.userId]: [...state.data[payload.userId], payload.message],
}
}

Add value to array in reducer

I am trying to add to an array in my object, I'm passing the id and the value to add:
case ADD_ACHIEVEMENT:
return [
{
id: action.id,
achievements: [...state.achievements, action.label]
},
...state
]
Initial state:
const initialState = [
{
date: "Fri 1st",
enjoyments: [],
achievements: [],
id: 0
},
{
date: "Fri 2",
enjoyments: [],
achievements: [],
id: 1
},
How can I add the value in the reducer?
EDIT: Sorry I'm new to redux I might not be explaining myself properly. I'd like to update the achievements array for the id that I pass in the action. My achievements array is just an array of strings. I would just like to add another entry.
Getting sytax error:
Syntax error: C:/sites/CalendarRedux/src/reducers/days.js: Unexpected token, expected , (62:11)
case ADD_ACHIEVEMENT:
return state.map(day => {
if (day.id === action.id) {
return Object.assign({}, day, {
achievements: [
...day.achievements,
action.label,
]
}; <<<<<<< line 62
}
return day;
});
case ADD_ACHIEVEMENT:
return state.map(item => {
if (item.id === action.id) {
return Object.assign({}, item, {
achievements: [
...item.achievements,
action.label,
]
});
}
return item;
});
Given your comment you basically want to update the item in your array that matches the id passed by the action. This is one way to do it, basically you map each item to itself if you don't wanna update it, and you update the one that matches the id. The error you were making was trying to access state.achievements while instead you needed to access the achievements of the specific item.

Redux reducer adding _id to state

I have a React / Redux application and am using redux-logger to show the state changes. I have some strange behaviour, wherby the resulting state contains an _id property. For example, this information below shows my output from the logger:
prev state:
{
title: ""
}
action object:
{
title: "New title"
}
next state:
{
_id: ObjectID,
title: "New title"
}
Below is my reducer function:
function updateState(state = initialState.obj, action) {
switch (action.type) {
case 'SET_TITLE':
var newState = Object.assign({}, state, action.obj);
return newState;
default:
return state;
}
}
and initialState is:
{
obj: {
title: ""
}
}
and the action payload is:
{
type: 'SET_TITLE',
obj: {
title: "New Title"
}
}
and the dispatcher is as follows:
// Call to API to get data from MongoDB...
let dataObj = response.data;
let newObj = {
title: dataObj.title
};
dispatch({
type: 'SET_TITLE',
obj: newObj
});
Why is the _id propery being added?
Nothing to worry about. Your initial state may even be an empty object.
{ }
And as your application traverses the states calling the reducer every time and creating new states
new_state = reducer_function(current_state, action);
Your Store object (state) may get different keys. You must try to understand what may enter the state, and to get the conclusion about the _id key.
Generally, the Store may be very complicated and big.
After you last update I understood the problem better, you are adding te object to the state.
var action = new Object;
action.title = "New Title";
var state = {
title: "Title",
desc: ""
}
console.log(state);
var newState = Object.assign({}, state, { title: action.title });
console.log(newState);
In your case "New Title" will be action.title.
This Object.assign() is new to ES6 btw.

Resources