简体   繁体   中英

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>;
}

There is a small mistake in your code. You should update cart outside of the map function instead of inside it when there are no matched object in the array.

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) {
        let dataExist = false;
        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);
                dataExist=true
            }
        });
        if(dataExist) {
            setCart([ ...cart, obj ]);
        }
    } else {
        setCart([ ...cart, obj ]);
    }
};

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

}

What your code doing was this, if the current item(e) from cart array doesn't match with obj, it was adding that obj in the array. Which should be done only after you have iterate the array and confirmed that there are no data exist in the array which is same as obj.

If that update doesn't solve your problem I might need some sample data(ie object structure, sample output etc...) from you to test this properly.

Please update your code with this one and it would be better if you can share obj data and cart data:

 const addItem = (obj) => { if (cart.length;== 0) { for (let i = 0. i <= cart;length. i += 1) { if (undefined.== cart[i]) { if (obj.productName === cart[i].productName) { const tempArr = [.;.cart]; tempArr;quantity += 1. setCart(tempArr). } else { setCart([.,;cart. obj]). } } } } else { setCart([.,;cart; obj]); } };

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!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM