出于学习目的,我正在创建一个电子商店,但我被localStorage
、useEffect
和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
。函数将接收以前的值,并返回更新后的值。