NextJS使用useContext实现购物车报错:store.push不是函数
购物车功能报错
store.push is not a function 排查与修复 问题根源
核心错误出在useShoppingCartContext中的addToCart函数:
const addToCart = (product) => { setStore(store.push(product)); };
Array.push()方法的返回值是数组的新长度(数字类型),而非更新后的数组。第一次点击按钮时,setStore会把store的状态从初始空数组[]改为数字1(添加第一个商品时的长度),此时组件无明显报错但状态已异常;第二次点击时,store是数字类型,调用push方法自然抛出store.push is not a function错误。
修复方案
修正后的Context代码
import { createContext, useCallback, useContext, useState, } from "react"; const ShoppingCartContext = createContext(); export function ShoppingCartContextProvider({ children }) { const [store, setStore] = useState([]); // 用useCallback包裹,避免子组件不必要重渲染 const addToCart = useCallback((product) => { // 函数式更新+扩展运算符,保持状态不可变性 setStore(prevStore => [...prevStore, product]); }, []); return ( <ShoppingCartContext.Provider value={{ store, addToCart, // 仅暴露必要方法,不直接暴露setStore }} > {children} </ShoppingCartContext.Provider> ); } export function useShoppingCartContext() { const context = useContext(ShoppingCartContext); if (!context) { throw new Error("useShoppingCartContext must be used within a ShoppingCartContextProvider"); } return context; }
关键修复点
- 使用函数式更新
setStore(prevStore => [...prevStore, product]):确保拿到最新的状态值,避免闭包导致的状态过期问题。 - 用扩展运算符创建新数组:符合React状态不可变原则,不直接修改原数组。
- 用
useCallback包裹addToCart:避免每次组件渲染都生成新函数实例,减少子组件不必要重渲染。 - 隐藏
setStore:将状态修改逻辑封装在Context内部,提升代码可维护性。
调用组件简化
移除不必要的store和setStore解构:
import { useRouter } from "next/router"; import { useState } from "react"; import { useShoppingCartContext } from "../../context/CartContext"; const SingleProduct = ({ product, categories }) => { const { addToCart } = useShoppingCartContext(); const router = useRouter(); const breadcrumb = router.components["/shop/[[...category]]"].resolvedAs; const [count, setCount] = useState(0); const increment = () => setCount(count + 1); const decrement = () => setCount(count - 1); return ( <> <Button onClick={() => addToCart(product)}> Add to Cart </Button> </> ) }
内容的提问来源于stack exchange,提问作者tanjim anim
相关产品推荐
相关产品推荐

