使用多个setState调用添加多个项时,状态中只添加一个项



出于学习目的,我正在创建一个电子商店,但我被localStorageuseEffect和React上下文卡住了。基本上,我有一个产品目录,其中每个项目都有一个button,应该将产品添加到购物车中。

它还在localStorage中创建一个对象,其中包含该商品的id和金额,您可以在将产品添加到购物车时选择该id和金额。

我的上下文文件:

import * as React from 'react';
const CartContext = React.createContext();
export const CartProvider = ({ children }) => {
const [cartProducts, setCartProducts] = React.useState([]);
const handleAddtoCart = React.useCallback((product) => {
setCartProducts([...cartProducts, product]);
localStorage.setItem('cartProductsObj', JSON.stringify([...cartProducts, product]));
}, [cartProducts]);
const cartContextValue = React.useMemo(() => ({
cartProducts,
addToCart: handleAddtoCart, // addToCart is added to the button which adds the product to the cart
}), [cartProducts, handleAddtoCart]);
return (
<CartContext.Provider value={cartContextValue}>{children}</CartContext.Provider>
);
};
export default CartContext;

当添加多个产品时,它们将正确显示在localStorage中。在添加了多个之后,我试图在控制台中记录cartProducts,但后来只记录了最近的一个,尽管localStorage中有多个。

我面临问题的组件:

const CartProduct = () => {
const { cartProducts: cartProductsData } = React.useContext(CartContext);
const [cartProducts, setCartProducts] = React.useState([]);
React.useEffect(() => {
(async () => {
const productsObj = localStorage.getItem('cartProductsObj');
const retrievedProducts = JSON.parse(productsObj);
if (productsObj) {
Object.values(retrievedProducts).forEach(async (x) => {
const fetchedProduct = await ProductService.fetchProductById(x.id);
setCartProducts([...cartProducts, fetchedProduct]);
});
}
}
)();
}, []);
console.log('cartProducts', cartProducts);
return (
<>
<pre>
{JSON.stringify(cartProductsData, null, 4)}
</pre>
</>
);
};
export default CartProduct;

我的带有fetchProductById功能的服务文件:

const domain = 'http://localhost:8000';
const databaseCollection = 'api/products';
const relationsParams = 'joinBy=categoryId&joinBy=typeId';
const fetchProductById = async (id) => {
const response = await fetch(`${domain}/${databaseCollection}/${id}?${relationsParams}`);
const product = await response.json();
return product;
};
const ProductService = {
fetchProductById,
};
export default ProductService;

到目前为止,我只想在控制台中看到我添加到购物车中的所有产品,但我只能看到最新的产品。有人能看出我的错误吗?或者我错过了什么?

这看起来很糟糕:

Object.values(retrievedProducts).forEach(async (x) => {
const fetchedProduct = await ProductService.fetchProductById(x.id);
setCartProducts([...cartProducts, fetchedProduct]);
});

您运行了一个循环,但cartProducts在每次迭代中都有相同的值

要么这样做:

Object.values(retrievedProducts).forEach(async (x) => {
const fetchedProduct = await ProductService.fetchProductById(x.id);
setCartProducts(cartProducts => [...cartProducts, fetchedProduct]);
});

或者这个:

const values = Promise.all(Object.values(retrievedProducts).map(x => ProductService.fetchProductById(x.id)));
setCartProducts(values)

最后一个更好,因为它可以减少的状态更新

打印useEffect内部的cartProducts,查看是否看到所有数据

useEffect(() => {
console.log('cartProducts', cartProducts);
}, [cartProducts]);

如果此行的返回将更正值

const productsObj = localStorage.getItem('cartProductsObj');

那么错误将出现在if条件中:用替换

(async () => {
const productsObj = localStorage.getItem('cartProductsObj');
const retrievedProducts = JSON.parse(productsObj);
if (productsObj) {
Object.values(retrievedProducts).forEach(async (x) => {
const fetched = await ProductService.fetchProductById(x.id);
setCartProducts(cartProducts => [...fetched, fetchedProduct]);
});
}
}

问题

例如,当您在循环中多次调用状态设置器时,React使用所谓的自动批处理,因此只有多次调用的给定状态设置器的最后一次调用才适用。

解决方案

CartProduct中的useEffect组件中,调用setCartProducts,为其提供一个函数更新程序,如下所示:

setCartProducts(prevCartProducts => [...prevCartProducts, fetchedProduct]);

即使React没有重新渲染,函数更新程序也总是获得最近的状态。React文档显示:

如果使用以前的state计算新的state,则可以将函数传递给setState。函数将接收以前的值,并返回更新后的值。

最新更新