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
}

在这个项目中,我想维护当用户单击添加按钮时已添加的购物车的状态.即使我用减速器正确注册了store ,cartReducer也无法维护点击按钮后添加的物品.到目前为止,我试图添加redux-persist,但当我点击添加按钮并刷新时,状态仍然无法保持.我已经try 了无数次,按照重复的文档操作,但仍然被困在这里,所以有人能提供帮助来解决这个问题吗?

推荐答案

您似乎已经实现了大约half个持久性设置.缺少的是创建一个persistor对象,可能还有PersistGate.

import { configureStore, combineReducers } from "@reduxjs/toolkit";
import {
  persistStore, // <-- add import
  persistReducer,
  FLUSH, // <-- import actions to ignore in serializability check
  REHYDRATE,
  PAUSE,
  PERSIST,
  PURGE,
  REGISTER,
} from 'redux-persist';
import storage from 'redux-persist/lib/storage';

import { userAPI } from "./api/userAPI";
import { userReducer } from "./reducer/userReducer";
import { productAPI } from "./api/productAPI";
import { cartReducer } from "./reducer/cartReducer";

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.name]
};

const persistedReducer = persistReducer(persistConfig, reducers)

export const store = configureStore({
  reducer: persistedReducer,
  middleware: (getDefaultMiddleware) => 
    getDefaultMiddleware({
      serializableCheck: { // <-- configure serializability middleware
        ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
      },
    }).concat([
      userAPI.middleware,
      productAPI.middleware
    ]),
});

export const persistor = persistStore(store); // <-- export persistor

export type RootState = ReturnType<typeof store.getState>

然后在UI中,确保您在Redux Provider组件下使用PersistGate包装应用程序.

示例:

import { PersistGate } from 'redux-persist/integration/react';
import { store, persistor } from './path/to/redux/store';

const AppRoot = () => {
  return (
    <Provider store={store}>
      <PersistGate loading={null} persistor={persistor}>
        <App />
      </PersistGate>
    </Provider>
  );
};

Typescript相关问答推荐

如何设置绝对路径和相对路径的类型?

如何使用一个字段输入对象,其中每个键只能是数组中对象的id属性,而数组是另一个字段?

对数组或REST参数中的多个函数的S参数进行类型判断

如何避免推断空数组

如何在已知基类的任何子类上使用`extends`?

类型脚本可以推断哪个类型作为参数传递到联合类型中吗?

如何避免从引用<;字符串&>到引用<;字符串|未定义&>;的不安全赋值?

Angular 16将独立组件作为对话框加载,而不进行布线或预加载

打印脚本中正则函数和函数表达式的不同类型缩小行为

为什么在这种情况下,打字脚本泛型无法正确自动完成?

字符串文字联合的分布式条件类型

递归生成常量树形 struct 的键控类型

有没有什么方法可以使用ProvideState()提供多个削减器作为根减减器

为什么`map()`返回类型不能维护与输入相同数量的值?

替换typescript错误;X类型的表达式可以';t用于索引类型Y;带有未定义

如何在TypeScript中描述和实现包含特定属性的函数?

我如何键入它,以便具有字符串或数字构造函数的数组可以作为字符串或数字键入s或n

传入类型参数<;T>;变容函数

Next 13 + React 18 createContext 类型的构建问题

当并非所有歧视值都已知时,如何缩小受歧视联盟的范围?