You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript Redux购物车状态刷新丢失问题求助

购物车状态无法通过redux-persist持久化

我在TypeScript项目中,希望用户点击添加按钮后,购物车状态能持久保留。虽然已经正确注册reducer,也尝试使用redux-persist,但添加商品到购物车后刷新页面,cartReducer的状态仍会丢失。多次参考Redux文档尝试解决都没成功,恳请帮忙排查问题。


相关代码

redux/reducer/cartReducer.ts

import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { CartReducerInitialState } from "../../types/reducer-types";
import { CartItem, ShippingInfo } from "../../types/types";

const initialState: CartReducerInitialState = {
    loading: false,
    cartItems: [],
    subtotal: 0,
    tax: 0,
    shippingCharges: 0,
    discount: 0,
    total: 0,
    shippingInfo: {
        address: "",
        city: "",
        state: "",
        country: "",
        pinCode: "",
    },
};

export const cartReducer = createSlice({
  name: "cartReducer",
  initialState,
  reducers: {
    addToCart: (state, action: PayloadAction<CartItem>) => {
      state.loading = true;

      const index = state.cartItems.findIndex(
        (i) => i.productId === action.payload.productId
      );

      if (index !== -1) state.cartItems[index] = action.payload;
      else state.cartItems.push(action.payload);
      state.loading = false;
    },

    removeCartItem: (state, action: PayloadAction<string>) => {
      state.loading = true;
      state.cartItems = state.cartItems.filter(
        (i) => i.productId !== action.payload
      );
      state.loading = false;
    }
  },
});

export const {
  addToCart,
  removeCartItem
} = cartReducer.actions;

redux/store.ts

import { configureStore } from "@reduxjs/toolkit";
import { userAPI } from "./api/userAPI";
import { userReducer } from "./reducer/userReducer";
import { productAPI } from "./api/productAPI";
import { cartReducer } from "./reducer/cartReducer";
import { persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage'
import {combineReducers} from "@reduxjs/toolkit";

export const server = import.meta.env.VITE_SERVER;

const reducers = combineReducers({
    [userAPI.reducerPath]: userAPI.reducer,
    [productAPI.reducerPath]: productAPI.reducer,
    [userReducer.name]: userReducer.reducer,
    [cartReducer.name]: cartReducer.reducer           
});

const persistConfig = {
    key: 'root',
    storage,
    whitelist: ["cartReducer"]
};

const persistedReducer = persistReducer(persistConfig, reducers)

export const store = configureStore({
    reducer: persistedReducer,
    middleware: (getDefaultMiddleware) => 
        getDefaultMiddleware().concat([
            userAPI.middleware,
            productAPI.middleware
        ]),
});

export type RootState = ReturnType<typeof store.getState>

pages/Home.tsx

const { cartItems, subtotal, tax, total, shippingCharges, discount } = useSelector((state: { cartReducer: CartReducerInitialState }) => state.cartReducer)
    
    const { data, isError, isLoading } = useLatestProductsQuery("")
    // console.log(data)
    console.log(cartItems)

    const dispatch = useDispatch()

    const addToCartHandler = (cartItem: CartItem) => {
        if(cartItem.stock < 1) return toast.error("Out of Stock")
        dispatch(addToCart(cartItem))
        toast.success("Item added to cart")
    }
     {
                            data?.products.map((i) => (
                                <ProductCard key={i._id} productId={i._id} name={i.name} price={i.price}
                                category={i.category} stock={i.stock} handler={addToCartHandler} photo={i.photo} />
                            ))
                        }

components/ProductCard.tsx

type ProductProps = {
    productId: string
    photo: string
    name: string
    category: string 
    price: number
    stock: number
    handler: (cartItem: CartItem) => string | undefined
}

const ProductCard = ({
    productId,
    photo,
    name,
    category,
    price,
    stock,
    handler
}: ProductProps) => {
    return (
        <div className="product-card">
            <h3 className="category">{category.toUpperCase()}</h3>
            <img src={`${server}/${photo}`} alt={name} />
            <p>{name}</p>
            <span>HK$ {price}</span>
            <div>
                <button onClick={() => handler({
                    productId,
                    price,
                    name,
                    category,
                    photo,
                    stock,
                    quantity: 1
                })}>
                    <FaPlus />
                </button>

            </div>
        </div>
    )
}

types/types.ts

export type CartItem = {
    productId: string
    photo: string
    name: string
    category: string
    price: number
    quantity: number
    stock: number
}

问题排查与解决

你的代码存在两个核心问题,导致redux-persist无法正常工作:

1. 未在根组件包裹PersistGate

redux-persist需要通过PersistGate组件完成持久化状态的加载流程,否则刷新页面后无法从本地存储中恢复状态。

2. 默认中间件未关闭序列化检查

Redux Toolkit的默认中间件会检查状态是否可序列化,而redux-persist的内部状态会触发这个检查,导致状态持久化失败。


具体修复步骤

步骤一:修改redux/store.ts的中间件配置

export const store = configureStore({
    reducer: persistedReducer,
    middleware: (getDefaultMiddleware) => 
        getDefaultMiddleware({
          serializableCheck: false // 关闭序列化检查,避免redux-persist触发警告
        }).concat([
            userAPI.middleware,
            productAPI.middleware
        ]),
});

步骤二:在应用根组件添加PersistGate

以main.tsx为例,先导入所需依赖:

import { persistStore } from 'redux-persist';
import { PersistGate } from 'redux-persist/integration/react';
import { store } from './redux/store';

然后创建persistor实例并包裹应用:

const persistor = persistStore(store);

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <Provider store={store}>
      <PersistGate loading={null} persistor={persistor}>
        <App /> {/* 你的应用根组件 */}
      </PersistGate>
    </Provider>
  </React.StrictMode>,
);

额外检查

你的CartItem类型均为基础可序列化类型(字符串、数字),无需额外处理。如果后续添加了不可序列化类型(如Date、函数),需要在persistConfig中配置对应的转换规则。

完成以上修改后,刷新页面即可看到购物车状态被正常持久化。


内容的提问来源于stack exchange,提问作者Dominic Sham

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 17:57:18