快速点击购物车加减按钮时dispatch执行异常问题排查
解决方案见下文
购物车加减功能快速点击异常问题
我正在开发一套购物车商品加减系统,代码实现如下:
const handleClick = (i) => { if (i >= 0 && i <= itemQuantity) { console.log("i : " + i); setQty(i); } }; useEffect(() => { if (qty >= 1) { dispatch(addToCart(product._id, qty)); } else { dispatch(removeFromCart(product._id)); } }, [qty]); <button onClick={() => handleClick(qty - 1)}>减</button> <img src={product.image} alt={product.name} /> <button onClick={() => handleClick(qty + 1)}>加</button>
慢速点击按钮时运行结果符合预期:减到0后无法继续点击,购物车为空,效果如下:
但如果快速点击减按钮,即便点击次数刚好将数量减到0,也会出现异常:数量卡在1无法继续减少,异常效果如下:
相关的action实现代码如下:
export const addToCart = (productId, qty) => async (dispatch, getState) => { const { data } = await Axios.get(`/api/prints/${productId}`); const { cart: { cartItems }, } = getState(); dispatch({ type: CART_ADD_ITEM, payload: { name: data.name, image: data.image, price: data.price, countInStock: data.countInStock, product: data._id, seller: data.seller, qty, }, }); localStorage.setItem("cartItems", JSON.stringify(getState().cart.cartItems)); }; export const removeFromCart = (productId) => (dispatch, getState) => { dispatch({ type: CART_REMOVE_ITEM, payload: productId }); localStorage.setItem("cartItems", JSON.stringify(getState().cart.cartItems)); };
问题原因
这个问题是典型的异步竞态导致的:
你用到的addToCart是异步函数,内部需要发起接口请求获取商品数据,执行速度远慢于同步执行的removeFromCart。快速点击减按钮时,你会连续更新qty的值,比如从2减到1再减到0,对应的useEffect会连续触发两次:第一次qty=1的时候调用addToCart,第二次qty=0的时候调用removeFromCart。
因为addToCart要等接口返回才会执行后续的dispatch操作,所以反而会比removeFromCart晚完成:相当于你先把商品从购物车删掉了,后面又执行了一次添加数量为1的商品到购物车的操作,最后数量自然就卡在1动不了了。
解决方案
最简单的修复方案就是给按钮加个loading锁,上一次操作没跑完的时候禁止点击,避免连续触发:
dispatch(addToCart(product._id, qty)).then(() => setLoad(false)); <button disabled={load} onClick={() => { setLoad(true); handleClick(qty - 1); }} >
如果要做的更严谨,还可以把handleClick里的qty更新改成函数式更新,避免获取到旧的状态值,同时给useEffect加清理函数,取消未完成的请求彻底避免竞态问题。
内容的提问来源于stack exchange,提问作者Guillaume Ayad
相关产品推荐
相关产品推荐

