我正在我的网站上构建侧车功能,并基于包含购物车项目的本地存储阵列进行实时更新。我正在使用事件来检测更改,但我很难更新小计价格:
YourCart.js:
这是购物车的上部,它检索本地存储物品。每次在本地存储中修改项目时,它都会重新渲染。当它重新渲染时,它还会调度
"updateSubtotal"
事件。
function YourCart() {
//state to store the items retrieved from local storage
const [cartItems, setCartItems] = useState([]);
//add a trigger state to re-render the cart items
const [trigger, setTrigger] = useState(false);
useEffect(() => {
//retrieve the items from local storage
const cartItemsFromStorage = JSON.parse(localStorage.getItem(LOCALSTORAGEKEY)) || [];
setCartItems(cartItemsFromStorage);
//listens for AddToCart clicks, triggers re-render when clicks happen
updateCartEvent.addEventListener('updateCart', updateCartlistener);
//dispatches updateSubtotal every re-render
updateCartEvent.dispatchEvent('updateSubtotal', "subtotal update!" )
}, [trigger]);
//updateCart callback function
const updateCartlistener = (event) => {
//removes old listener before setting a new one (prevents leaks)
updateCartEvent.removeEventListener('updateCart', updateCartlistener);
setTrigger(!trigger);
};
仍然在同一个组件上,我想展示如何触发 useEffect。每个项目操作(添加、减去、删除)都会调用
updateLocalStorage()
,然后更新本地存储并触发组件重新渲染:
/*function to update local storage with the updated cart items, after that
trigger the component re-render with setCartItems*/
const updateLocalStorage = (updatedCartItems) => {
//updates local storage
localStorage.setItem(LOCALSTORAGEKEY, JSON.stringify(updatedCartItems));
//toggle the trigger state to force a re-render
setTrigger(!trigger);
};
//function to handle the removal of an item
const remove = (itemId) => {
removeFromGlobalState(itemId);
const updatedCartItems = cartItems.filter((item) => item._id !== itemId);
updateLocalStorage(updatedCartItems);
};
//function to handle the subtraction of quantity
const subtract = (itemId) => {
const updatedCartItems = cartItems.map((item) => {
if (item._id === itemId && item.quantity > 1) {
item.quantity -= 1;
}
return item;
});
updateLocalStorage(updatedCartItems);
};
//function to handle the addition of quantity
function add(itemId) {
const updatedCartItems = cartItems.map((item) => {
if (item._id === itemId) {
item.quantity += 1;
}
return item;
});
updateLocalStorage(updatedCartItems);
};
yourCartActions.js
这是购物车的下半部分,是显示总价的组件。它侦听
updateSubtotal
事件并从我的本地存储代码调用 calculateTotalPrice()
函数:
function YourCartActions(){
//initialize subtotalPrice with an initial value
const [subtotalPrice, setSubtotalPrice] = useState(calculateTotalPrice());
useEffect(() => {
//update subtotalPrice when the event is triggered
const handleCartUpdated = () => {
setSubtotalPrice(calculateTotalPrice());
console.log("Subtotal Updated!");
};
//add an event listener for 'updateSubtotal' event
updateCartEvent.addEventListener('updateSubtotal', handleCartUpdated);
//cleanup the event listener when the component unmounts
return () => {
updateCartEvent.removeEventListener('updateSubtotal', handleCartUpdated);
};
}, []);
问题:
当使用其他
AddToCart
组件添加项目以及使用 remove()
功能删除项目时,小计实际上会实时更新,但当我使用 add()
和 subtract()
时,小计不会实时更新
功能(仅当我刷新页面时)。
如您所见,我确实为每次调用
console.log("Subtotal Updated!");
时设置了 handleCartUpdated()
,然后我继续多次单击 add()
和 subtract()
,每次都会打印 Subtotal Updated!
,所以这意味着实际上正在调用更新价格的函数,但正如我所说,它仅在我刷新页面时才实际更新总价。
您不需要
trigger
状态。您的 useEffect
可以使用 cartItems
数组作为依赖项,并且每当数组更新时它都会更新。
由于每次修改数组时都会触发
useEffect
钩子,因此您可以将更新本地存储的代码放入此钩子中。您可以在这个官方文档中阅读useEffect如何工作。
您的购物车
const [cartItems, setCartItems] = useState([]);
// this hook stores into local storage
useEffect(() => {
localStorage.setItem(LOCALSTORAGEKEY, JSON.stringify(cartItems));
}, [cartItems]);
// this hook is only executed once to update your cart items
useEffect(() => {
const cartItemsFromStorage = JSON.parse(localStorage.getItem(LOCALSTORAGEKEY)) || [];
}, []);
setCartItems(cartItemsFromStorage);
但是,为了确保触发
useEffect
钩子,您必须像这样更新数组:
function add(itemId) {
const updatedCartItems = cartItems.map((item) => {
if(item.id === itemId) {
return {
...item,
quantity: item.quantity + 1
}
}
});
}
map
返回一个新数组,因此它将触发您的useEffect
再次运行。
您不需要使用自定义事件侦听器来处理购物车中的添加/删除/更新事件。请阅读此有关处理事件的 React 文档
为了确保
CartActions
组件正确计算小计,只需将您的 cartItems
传递给它即可。
您的购物车
// ... other code
return (
<YourCartActions cartItems={cartItems} />
{ /* other jsx elements */ }
)
您的购物车操作
function YourCartActions({ cartItems }) {
const [subtotal, setSubtotal] = useState(0);
useEffect(() => {
setSubtotal(calculateTotalPrice()); // or just calculate here
}, [cartItems])
}
//...other code