Redux : Updating object values inside array immutably - reactjs

const initialState = {
arr: [
{
name: "Chicken",
grade: "A",
quantity: 0
},
{
name: "Mutton",
grade: "B",
quantity: 0
},
{
name: "Sandwich",
grade: "A-Plus",
quantity: 0
}
]
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case actionTypes.ADD_QUANTITY:
return {
...state,
arr: {
...state.arr,
[state.arr[action.index]]: {
...state.arr[action.index],
[state.arr[action.index][0].quantity]:
[state.arr[action.index][0].quantity] + 1
}
}
};
default:
return state;
}
};
I'm trying to update the quantity immutably. Each time I click on a button, the quantity should be increased by 1. The above code which I have written is wrong (hence posting the query here). I'd appreciate it if anyone could show me where I went wrong and point me in the right direction.
The final output I am expecting is:
arr: [
{
name: "Chicken",
grade: "A",
quantity: 1 // Updated value
},
{
name: "Mutton",
grade: "B",
quantity: 0
},
{
name: "Sandwich",
grade: "A-Plus",
quantity: 0
}
]

There are a few things that won't work with your current code. Your initial state defines arr as an Array, but your reducer is returning an Object instead. Also, when you are trying to access the quantity key of the object inside arr, you are using an additional [0] index accessor that doesn't match with your data structure.
I would also suggest that you compose reducers (using combineReducers), to make it easier for you to keep track of the data structure. Combining reducers lets you handle individual levels of your data structure without having to worry about the whole structure. Also, using the spread operator works well for objects, but functions like map sometimes are clearer when manipulating arrays.
Something like this would do what you need:
const arr = (state = initialState.arr, action) => {
switch (action.type) {
case actionTypes.ADD_QUANTITY:
return state.map((element, i) => {
return i === action.index ? {...element, quantity: element.quantity + 1} : element;
});
default:
return state;
}
}
const rootReducer = combineReducers({arr});

Related

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
}
*/

How to update Global state correctly?

I'm working on an e-comerce type application. I was implementing the cart functionality but I got stuck and can't seem to find a way.
Basically I've a global state called cart. which looks like this:
cart: [
{
book: {},
quantity: 1
}
]
I want to update the quantity when a book is found in it.
case "ADD_TO_CART":
let book_index = state.cart.findIndex(
(el) => el.book._id === action.payload._id
);
let newCart;
if (book_index === -1){
newCart = state.cart.concat({book: action.payload, quantity: 1})
} else {
newCart = [...state.cart];
newCart[book_index].quantity = newCart[book_index].quantity + 1;
}
return {
...state,
cart: newCart,
};
The problem in it is that when a book is found, it updates the quantity by a factor of 2. So the quantity goes like 1, 3, 5, 7 so on.
I tried to replace 1 by 0.5 to see if it's doubling it and it's not.

Delete multiple item from array - Redux State

I'm working on react app with redux. I want to delete multiple item from array. I write below code in my reducer which delete single item from array but i want to delete multiple item.
case DELETE_LINK:
let dltLink = state.filter(item => {
return item._id !== action.data._id
})
return {
...state,
parentFolderlinks: dltLink
};
It seems you want to filter links from state.parentFolderlinks, say you have the ids in action.data.ids, you could
case DELETE_LINK:
const parentFolderlinks = state.parentFolderlinks.filter(item => {
return !action.data.ids.includes(item._id);
});
return {
...state,
parentFolderlinks
};
On what basis would you like to filter items? I assume that multiple items will not have the same id.
Below example shows how we can filter multiple items in redux. In this case, foods state with items that has type as fruit and removes everything else.
// initial state with all types of foods
const initialState = {
"foods": [
{
name: "apple",
type: "fruit"
},
{
name: "orange",
type: "fruit"
},
{
name: "broccoli",
type: "vegetable"
},
{
name: "spinach",
type: "vegetable"
},
]
}
// sample reducer that shows how to delete multiple items
export default (state = initialState, { type, payload }) => {
switch (type) {
// delete multiple items that does not have type fruit
// i.e both brocolli and spinach are removed because they have type vegetable
case DELETE_ITEMS_WITHOUT_TYPE_FRUIT:
const onlyFruits = state.foods.filter(food => food.type === "fruit");
return {
...state,
foods: onlyFruits
}
}
}
you could map over the state and run it through a function that works out if you want to keep it or not (I don't know what your logic is for that) then return the array at the end
const keepThisItem =(item) => {
return item.keep
}
case DELETE_LINK:
let itemsToKeep = []
let dltLink = state.map(item => {
if(keepThisItem(item){
itemsToKeep.push(item)
}
return itemsToKeep
})

Overwriting array of objects instead of updating it

Im currently learning redux and trying few options out. Everything looks okay until I want to update one state in the array of objects.
I'm dispatching 5 actions in total now, first 2 setting longitutde and latitude for one part of the state using one reducer, then I set IDs with other reducer and finally when I want to update one of the objects in the array I update one but delete the other somehow.
My file looks like this:
const demoState = {
renderedDrugs: [
{
id: '',
drugId: '',
price: undefined
}
],
apt: {
latA: '',
lonA: ''
}
}
//SET_ID
const setId = (id, drugId) => ({
type: 'SET_ID',
renderedDrugs: {
id,
drugId
}
})
//SET_PRICE
const setPrice = (drugId, price) => ({
type: 'SET_PRICE',
drugId,
price
})
//RENDERED DRUGS REDUCER
const renderedDrugsReducerDefState = [];
const renderedDrugsReducer = (state = renderedDrugsReducerDefState, action) => {
switch (action.type) {
case 'SET_ID':
return [
...state,
action.renderedDrugs
]
case 'SET_PRICE':
return state.map((drug) => {
if (drug.drugId === action.drugId) {
return {
...drug,
...action.price
}
}
})
default:
return state;
}
}
//SET_LAT
const setLat = (latA) => ({
type: 'SET_LAT',
latA
})
//SET_LON
const setLon = (lonA) => ({
type: 'SET_LON',
lonA
})
//APT REDUER
const aptReducerDefState = []
const aptReducer = (state = aptReducerDefState, action) => {
switch (action.type) {
case 'SET_LAT':
return {
...state,
latA: action.latA
}
case 'SET_LON':
return {
...state,
lonA: action.lonA
}
default:
return state;
}
}
//STORE CREATION
const store = createStore(
combineReducers({
renderedDrugs: renderedDrugsReducer,
apt: aptReducer
})
)
store.subscribe(() => {
console.log('store', store.getState());
})
store.dispatch(setLat(88));
store.dispatch(setLon(78));
store.dispatch(setId(uuid(), 3));
store.dispatch(setId(uuid(), 35));
store.dispatch(setPrice(35, {price: 400}));
I assume the SET_PRICE action is at fault, but I tried various configurations and cant figure out the issue so thats why I posted the whole file, if thats unnecessary let me know and I will delete irrelevant bits.
Console log after 4th dispatch:
{renderedDrugs: Array(2), apt: {…}}
apt
:
{latA: 88, lonA: 78}
renderedDrugs
:
Array(2)
0
:
{id: "2a3c4bca-610a-4554-b7e3-695ae6e30ae7", drugId: 3}
1
:
{id: "48df057a-c8f1-4138-8db7-6268f7508ccb", drugId: 35}
length
:
2
__proto__
:
Array(0)
__proto__
:
Object
and aftr 5th
{renderedDrugs: Array(2), apt: {…}}
apt
:
{latA: 88, lonA: 78}
renderedDrugs
:
Array(2)
0
:
undefined
1
:
{id: "48df057a-c8f1-4138-8db7-6268f7508ccb", drugId: 35, price: 400}
length
:
2
__proto__
:
Array(0)
__proto__
:
Object
The .map doesn't return the unchanged objects for the items your're not updating. Adding a return should fix it:
return state.map((drug) => {
if (drug.drugId === action.drugId) {
return {
...drug,
...action.price
}
}
return drug; // this was missing before, so the return was undefined
})

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.

Resources