Search code examples
javascriptreactjsnext.jsimmutability-helper

Update nested object in array in ReactJs


I am having trouble updating a nested object in an array in react state.

I am trying to compare whether or not an item already exists in the state based off of the name of that item.

Here's what I am trying to have happen:

  1. Add item to cart. If the product name is the same, then increase qty by one. (this works)

  2. If the name is not the same then add that new item to the state. (this also works)

  3. If I go back to a previous item that I have already added, and want to add it again, then I want to find that item in state, compare it to the current object that is being passed in, and update the quantity by one. (this doesn't work)

I decided to implement the immutability-helper to simplify updating state.

I should note, that I am using NextJs and that this problem occurs only after I reload a new dynamic page.

Hope this is enough information... please let me know if more info is needed to be of assistance.

Thanks in advance!

Update 2: This is another problem that I am running into... maybe the answer is obvious, but I thought I'd just put it out there to get a second set of eyes on it (if this should be a new question, please let me know).

I have some optional parameters that go along with each item. The parameters are a nested object inside of the array. My solution was to use the following code to compare the data.

import React, { createContext, useState, useEffect } from 'react';
import update from 'immutability-helper';
export const CartContext = createContext();

export function CartProvider(props) {
    const [ cart, setCart ] = useState([]);

//**********Start of the code to check the parameters of each item********

    const checkArr = (obj1, obj2) => {
        let newArr = [];
        function onlyInFirst(first, second) {
            for (let i = 0; i < first.length; i++) {
                if (second.indexOf(first[i]) === -1) {
                    newArr.push(first[i]);
                }
            }

        }

        onlyInFirst(obj1, obj2);
        onlyInFirst(obj2, obj1);

        if (newArr.length === 0) {
            return false;
        } else {
            return true;
        }
    };

//*******End of the code to check the parameters of each item**********

    const addItem = (obj) => {
        let dataCheck = true;
        if (cart.length != 0) {
            cart.map((e, i) => {
                if (e.productName === obj.productName) {
                    const prevVal = Object.values(e.productParams);
                    const currentVal = Object.values(obj.productParams);
                    dataCheck = checkArr(prevVal, currentVal);
                }
                if (dataCheck === false) {
                    const object = e;
                    const cartCopy = cart;
                    const newObj = update(object, { quantity: { $set: object.quantity + 1 } });
                    const newState = update(cartCopy, { [i]: { $set: newObj } });
                    setCart(newState);
                }
            });
        } else {
            setCart([ ...cart, obj ]);
        }
        if (dataCheck === true) {
            setCart([ ...cart, obj ]);
        }
    };

    return <CartContext.Provider value={{ cart, addItem }}>{props.children}</CartContext.Provider>;
}

However, I'm still getting the same sample output as shown below, regardless of what parameters I add to productParams.

Does anyone see anything wrong with my logic here? I am at a loss of what to do...

UPDATE 1: I'm adding the object structure and sample output.

Object structure:

const obj = {
    productName: product.name, // product.name comes from api
    productParams: {}, // this is dynamically added elsewhere
    quantity: 1,
    productPrice: price 
}

Sample Output From Chrome Dev Tools:

3) [{…}, {…}, {…}]
0: {productName: "Item 1", productParams: {…}, quantity: 4, productPrice: 60}
1: {productName: "Item 2", productParams: {…}, quantity: 3, productPrice: 3000}
2: {productName: "Item 1", productParams: {…}, quantity: 3, productPrice: 60}
length: 3
__proto__: Array(0)
import React, { createContext, useState, useEffect } from 'react';
import update from 'immutability-helper';
export const CartContext = createContext();

export function CartProvider(props) {
    const [ cart, setCart ] = useState([]);

    const addItem = (obj) => {
        if (cart.length != 0) {
            cart.map((e, i) => {
                if (e.productName === obj.productName) {
                    const object = e;
                    const cartCopy = cart;
                    const newObj = update(object, { quantity: { $set: object.quantity + 1 } });
                    const newState = update(cartCopy, { [i]: { $set: newObj } });
                    setCart(newState);
                } else {
                    setCart([ ...cart, obj ]);
                }
            });
        } else {
            setCart([ ...cart, obj ]);
        }
    };

    return <CartContext.Provider value={{ cart, addItem }}>{props.children}</CartContext.Provider>;
}


Solution

  • I solved it! InsomniacSabbir had the right idea. I just had to modify the code a bit to get the result I wanted.

    Here's the solution

    import React, { createContext, useState, useEffect } from 'react';
    import update from 'immutability-helper';
    export const CartContext = createContext();
    
    export function CartProvider(props) {
        const [ cart, setCart ] = useState([]);
    
        const addItem = (obj) => {
            let dataCheck = true;
            if (cart.length != 0) {
                cart.map((e, i) => {
                    if (e.productName === obj.productName) {
                        const object = e;
                        const cartCopy = cart;
                        const newObj = update(object, { quantity: { $set: object.quantity + 1 } });
                        const newState = update(cartCopy, { [i]: { $set: newObj } });
                        setCart(newState);
                        dataCheck = false;
                    }
                });
            } else {
                setCart([ ...cart, obj ]);
            }
            if (dataCheck === true) {
                setCart([ ...cart, obj ]);
            }
        };
    
        return <CartContext.Provider value={{ cart, addItem }}>{props.children}</CartContext.Provider>;
    }
    

    I had an if/else statement in map which was causing the problem. I took out the else statement out of map and added another if statement into the function that checks if dataCheck is true/false. dataCheck would be set to false only if the if statement in map was executed.

    Hope this answer helps!

    Thanks for the help everyone!