React useState 不会自动重新渲染以反映数据库中更新的对象

问题描述 投票:0回答:1

我正在开发我的第一个 React 项目,其中实现了购物车功能。当用户单击按钮增加购物车中商品的数量时,我会通过后端路由 (addCart) 更新 MongoDB 数据库中的数量。更新数据库中的数量后,我想重新渲染组件以反映总价的变化,但状态没有相应更新。

  • 我有一个 cartItems 状态,其中包含表示用户购物车中的项目的多个对象。
  • 当用户单击按钮增加数量时(handleAddCounter函数),我调用quantityUpdater来更新数据库中的数量。
  • 更新数据库中的数量后,我更新 cartItems 状态并调用 calculateTotalPrice 来更新总价。
  • 但是,直到我刷新页面后,总价才反映更新的数量。 我怀疑问题在于数据库更改后我如何更新状态。有人可以指导我如何正确更新状态并在更新数据库后触发重新渲染吗?
//My states and their initial values
const [cartItems, setCartItems] = useState([]); //This currently holds multiple objects
const [totalPrice, setTotalPrice] = useState(0);
//Button that adds to the quantity
<button className='inline-flex items-center justify-center font-medium text-xl text-slate-600 mx-2 h-8 w-8 hover:border hover:bg-gray-300 hover:border-gray-300 hover:rounded-full' onClick={() => {handleAddCounter(item.productId, item.productImage, item.productTitle, item.productPrice, item.productSize, item.quantity)}}>+</button>
//Trying to run fetch data and calculate price on each re-render
useEffect(() => {
        fetchCartData();
        //Running calculate total price to get the new value from the state
        calculateTotalPrice();
    }, [cartItems]);

//Updating the quantity
const handleAddCounter = (prodId, img, title, price, size, quantity) => {
const updatedQuantity = quantity + 1;
quantityUpdater(prodId, img, title, price, size, quantity, updatedQuantity);}
//Sending that to the back end
const quantityUpdater = async (prodId, img, title, price, size, quantity, updatedQuantity) => {
try {
const token = localStorage.getItem('token');
console.log('Getting token from singleProduct: ', token);
const showUserName = username;console.log('Seeing if username has a value: ', showUserName);
const showProductId = prodId;console.log('Seeing if product id has a value: ', showProductId);
        if (!token) {
            throw new Error('Authentication token not found');
        }

        const response = await fetch('http://localhost:3001/auth/user/addCart', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${token}`,

            },
            body: JSON.stringify({
                productId: prodId,
                img: img,
                title: title,
                price: price,
                size: size,
                quantity: updatedQuantity,
                username,
            }),
        });

        if (!response.ok) {
            throw new Error(`Network response error: ${response.statusText}`);
        }

        // Update cartItems state with the new data (forcing state to re-render because it doesn't recognize an updated object)
        // React only recognizes a state change when the state is se t with a new object
        const updatedCartItems = cartItems.map(item => {
            if (item.productId === prodId && item.productSize === size) {
                // Update quantity for matching product ID and size
                return {
                    ...item,
                    quantity: updatedQuantity
                };
            }
            // Return unchanged item for other items
            return item;
        });

        setCartItems(updatedCartItems);
        calculateTotalPrice();
        console.log('Calculate Total price fired!');
    } catch (error) {
        console.error('Error adding to cart: ', error.message);
    }
};

 //Calculating the total price 
const calculateTotalPrice = () => { 
console.log('Calculate total price received and running'); 
// Check if cart is defined and is an array 
if (cartItems && Array.isArray(cartItems)) { 
let addedValue = 0; 
console.log('This is to check the calculated price to see if cartItems is actually printing something worth while: ', cartItems); 
for (let i = 0; i < cartItems.length; i++) { 
// Check if cart[i] is not undefined before accessing its properties 
if (cartItems[i] && cartItems[i].productPrice) { addedValue += cartItems[i].productPrice; } }
setTotalPrice(addedValue);

        console.log('Is total Price updating the price?: ', totalPrice);
    } else {
        setTotalPrice(0);
    }
}

//Back end to create new item or update an existing object 
router.post('/addCart', authenticate, async (req, res) => { 
const { productId, img, title, price, size, quantity, username } = req.body; 
//Extracting product information from 
console.log('Product ID in route:', productId); 
console.log('IMG in route:', img); 
console.log('title in route:', title); 
console.log('Price in route:', price); 
console.log('Size in route:', size); 
console.log('Quantity in route:', quantity); 
console.log('username in route:', username); 
// Extract productId from req.body 
const isProductIdANum = req.body.productId; console.log('Type of productId:', typeof isProductIdANum);
//Price being manipulated below
let newPrice;

// Check if they exist, I don't want to go further if they don't
if (!productId || !img || !title || !price || !size || !quantity || !username) {
    return res.status(400).json({ error: 'Missing required fields' });
}

try {
    //Querying the username field in the user collection
    const user = await User.findOne({ username });

    if (!user) {
        return res.status(404).json({ error: 'User not found' });
    }

    //Check for existing cart item
    //The productId being produced from req.body is a string so we converted it back to int
    const existingCartItem = user.cart.find(item => item.productId === parseInt(productId));
    const existingSize = user.cart.find(item => item.productSize === parseInt(size));

    if (existingCartItem && existingSize) {
        //Changing the quantity value instead of adding another object
        console.log('Existing item found!: ', existingCartItem);
        //Storing the original price 
        const originalPrice = existingCartItem.productPrice / existingCartItem.quantity;
        console.log('Original Price: ', originalPrice);

        //updating the quantity
        existingCartItem.quantity = quantity;
        console.log('Quantity: ', quantity);

        //Price Based on Quantity
        newPrice = originalPrice * quantity;
        console.log('New Price: ', newPrice)

        existingCartItem.productPrice = newPrice;
    } else {
        // Adding the product to the cart
        productPrice = price * quantity;
        user.cart.push({ productId, productImage: img, productTitle: title, productPrice: productPrice, productSize: size, quantity: quantity });
    }
    await user.save();

    res.status(201).json({ message: 'Product added successfully to cart' });
} catch (error) {
    console.error('Error adding to cart: ', error.message);
    res.status(500).json({ error: 'Could not add to cart' });
}

});

javascript reactjs mongodb react-hooks state
1个回答
0
投票

由于totalPrice可以直接从购物车中计算出来,所以它不应该是它自己的状态。相反,在渲染期间计算它。这将减少渲染数量,使状态不可能不同步,并且作为奖励,它将解决您在计算中使用过时的闭包变量时遇到的问题。

const [cartItems, setCartItems] = useState([]);
let totalPrice = 0;
if (cartItems && Array.isArray(cartItems)) {
  for (let i = 0; i < cartItems.length; i++) {
    if (cartItems[i] && cartItems[i].productPrice) {
      totalPrice += cartItems[i].productPrice;
    }
  }
}

如果计算成本很高,您可以将其包装在

useMemo
中,这样只有当 cartItems 发生变化时才会重新计算。

const [cartItems, setCartItems] = useState([]);
const totalPrice = useMemo(() => {
  let totalPrice = 0;
  if (cartItems && Array.isArray(cartItems)) {
    for (let i = 0; i < cartItems.length; i++) {
      if (cartItems[i] && cartItems[i].productPrice) {
        totalPrice += cartItems[i].productPrice;
      }
    }
  }
  return totalPrice;
}, [cartItems]);
© www.soinside.com 2019 - 2024. All rights reserved.