Redux - Adding element to array nested in an object inside an array - arrays

Can't figure out how to properly insert an element into an array, that's inside an object, that's inside an array. Here's an example of my default data for structure:
const defaultState = {
myinbox: [
{
owner: 'John Lennon',
owner_id: 1,
read: true,
messages: [
{
_id: 1,
text: 'When will you be home?',
createdAt: new Date(Date.UTC(2017, 10, 11, 11, 20, 0)),
user: {
_id: 1,
name: 'John Lennon'
}
}
...
I want to add another message when an inbound message comes in. This is what the snippet from my reducer looks like:
const inboxReducer = (state = defaultState, action) => {
switch (action.type) {
case 'ADD_INBOUND_MESSAGE':
return {
...state,
myinbox: [
state.myinbox[action.payload.index]: {
...state.myinbox[action.payload.index],
messages: [
...state.myinbox[action.payload.index].messages,
action.payload.msg
]
}
...state.myinbox,
]
}
default:
return state
}
}
The index of the parent "owner" is passed as the index inside the payload, and the new message is msg in the payload.
I can't figure out how to write this reducer without mutating the original state.

You're mutating the original state when you set myinbox using state.myinbox[action.payload.index]:.
Looks like you're trying to set the state for the index using computed property keys. The syntax for that would be:
myinbox: [
[action.payload.index]: {
...state.myinbox[action.payload.index],
messages: [
...state.myinbox[action.payload.index].messages,
action.payload.msg
]
}
...state.myinbox,
]

This can be done with Immer
const { index, msg } = action.payload;
return produce(state, (draftState) => {
draftState.myinbox[index].messages.push(msg);
});

Related

update array inside object inside another array

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

How to get old state of an array that is inside and object with spread operator in Redux

i'm tryin to get the old state of my productData[] with spread operator but this array is inside in object.
I have to keep old state because i need to keep my products that are actually in state and to add new products here in Redux.
i try this:
case 'ADD_TO_COMPARE':
return {
productData:[...state.productData,action.payload],
open:true,
}
but didnt work and here is only last product i put here.
Here is my code in reducer on Redux:
const initialValue = {
productData:[],
open:false
}
export const compareReducer = (state = initialValue, action) => {
switch (action.type) {
case 'ADD_TO_COMPARE':
return {
productData:[...state.productData,action.payload],
open:true,
}
case 'REMOVE_FROM_COMPARE':
return initialValue
default:
return state
}
}
My Console:
2 first products are added hard code and the when the third product is added, always override the last product that is added here in state
I don't see an obvious issue with the way you're merging the productData. I suspect either state.productData is empty on the way in or action.type isn't a match.
Syntactically what you're doing works:
// old state
const state = {
productData: [{id: 1}, {id: 2}],
otherStuff: 'bananas and wookies'
}
// action
const action = {
payload: [{id: 3}, {id: 4}]
}
const merged = { // new object literal
...state, // keep old state properties
productData: [ // overwrite the 'productData' from state with
...state.productData, // the previous state's product data
...action.payload // and the action's payload
]};
console.log(merged);
/*
{
"productData": [
{ "id": 1 }, <-- original state
{ "id": 2 },
{ "id": 3 }, <-- action payload
{ "id": 4 }
],
"otherStuff": "bananas and wookies" <-- original state
}
*/

immutable helper updating value in object in array

I'm trying to understand immutable helper in the context of what I'm trying to do.
I'm trying to update the objects inside based on when an onChange event calls the INPUT_CHANGE action. it should add to the formData value not replace it. I tried $add but that didn't work the way I wanted it either.
Also every time a a new input filed is changed it adds a new object with the same updated changes.
Array[index] -> input1 -> INPUT_CHANGE -> update object with key and value of input field as it changes.
inputs=[{key:{key:'input name', value: mew}}]
Array[index] -> input2 -> INPUT_CHANGE -> update object with key and value of input field as it changes.
inputs=[{key:{key:'input name', value: mew}}, {key:{key:'input name', value: mew}}]
and so on ...
const setupState = {
count: 0,
inputs: [{}],
};
export default (state = setupState, action) => {
switch (action.type) {
case INPUT_CHANGE: {
// const formDataArr = state.inputs[count];
return update(state, {
inputs: [{
key: { $set: action.key },
value: { $set: action.value },
}],
});
}
default: return state;
}
};
Kind of confused as to how to use immutable helper for this ?
UPDATE:
This is how the the object should look as user updates each input.
inputs: [
{
"score": {
"key": "score",
"value": "20....",
},
"hits": {
"key": "hits",
"value": "ss..",
}
}
]
Try this.
export default (state = setupState, action) => {
switch (action.type) {
case INPUT_CHANGE: {
// const formDataArr = state.inputs[count];
return update(state, {
inputs: [
// contain origin state.inputs!!!!!!!!!!!!!!!!!!!
...state.inputs,
{
key: { $set: action.key },
value: { $set: action.value },
}],
});
}
default: return state;
}

Redux's Reducer - updating with keys as numbers

In my current implementation, I have an initial state as so:
const state = {
1: {id: 1, status: false},
2: {id: 2, status: false}
}
I'm having a hard time how to implement the reducer correctly if I wanted to update the status on either the item.
I have the following as so:
export default function (state = initialState, action) {
switch (action.type) {
case UPDATE_TO_TRUE: {
const { itemID } = action;
let item = itemID.toString();
return {
...state,
item: {
status: true
}
}
}
default: {
return state;
}
}
}
Essentially I can't figure out how to pinpoint the right key. What's tricky is that the keys are setup as numbers.
Try using a computed property key in the object literal definition:
return {
...state,
[item]: {
id: item,
status: true
}
}
You can access the item you want using bracket notation, which works as long as the key can be represented by a string. So for example:
const state = {
1: {id: 1, status: false},
2: {id: 2, status: false}
};
console.log(state['2']);
returns your second item.

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.

Resources