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

使用Redux Toolkit无法查看购物车商品的技术问题排查

问题描述

我正在开发一个React小项目,计划用Redux Toolkit实现购物车逻辑。需求是:点击“Add To Bag”按钮时,将商品的数量、颜色、名称、品牌等信息添加为购物车中的一件商品,打开购物车页面时能渲染这些商品。

相关代码

Index.js

import { Provider } from "react-redux";
import { store } from "./Redux/Store";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <>
    <Provider store={store}>
      <App />
    </Provider>
  </>
);     

Store.js

import { configureStore } from "@reduxjs/toolkit";
import cartReducer from "./Slices/CartSlices";

export const store = configureStore({
  reducer: {
    cart1: cartReducer,
  },
});

CartSlices.js

import { createSlice } from "@reduxjs/toolkit";

const cartSlice = createSlice({
  name: "Cart",
  initialState: [],
  reducers: {
    addItem: (state, action) => {
      state.push(action.payload);
    },
  },
});
export const { addItem } = cartSlice.actions;
export default cartSlice.reducer;

SingleProduct.js

import React, { useEffect, useState } from "react";
import { Link, useLocation } from "react-router-dom";
import "./SingleProduct.css";
import { addItem } from "../Redux/Slices/CartSlices";
import { useDispatch } from "react-redux";

function SingleProduct() {
  const location = useLocation();
  const productData = location.state?.data;
  const dispatch = useDispatch();

  const [selectedColorIndex, setSelectedColorIndex] = useState(null);
  const [selectedQuantity, setSelectedQuantity] = useState(0);

  const QuantitySelect = () => {
    const options = Array.from({ length: 10 }).map((_, i) => (
      <option key={i} value={i}>
        {i}
      </option>
    ));
    return options;
  };

  return (
    <section>
      <div className="row">
        <div className="col-md-6">
          <img
            src={productData.attributes.image}
            alt=""
            className="mt-5 rounded-5 ms-5 shadow"
            style={{
              objectFit: "cover",
              height: "400px",
              width: "550px",
            }}
          />
        </div>
        <div className="col-md-6 mt-5">
          <h2 className="fw-bold">{productData.attributes.title}</h2>
          <h5 className="fw-bold text-black-50 mt-3">
            {productData.attributes.company}
          </h5>
          <p className="mt-3">
            <span>$</span>
            {productData.attributes.price}
          </p>
          <p className="lead fs-6 fw-light lh-lg">
            {productData.attributes.description}
          </p>
          <div>
            <p className="fw-bold"> Colors</p>
            <div className="d-flex">
              {productData.attributes.colors.map((color, index) => (
                <div
                  key={index}
                  className={`dot ${selectedColorIndex === index ? "selected-dot" : ""}`}
                  style={{ backgroundColor: color }}
                  onClick={() => setSelectedColorIndex(index)}
                ></div>
              ))}
            </div>
            <div>
              <p className="mt-3 fw-bold">Quantity</p>
              <select
                className="form-select w-50 rounded-3"
                value={selectedQuantity}
                onChange={(e) => setSelectedQuantity(e.target.value)}
              >
                <QuantitySelect />
              </select>
            </div>

             {/* <Link to="/cart"> */}
              <div
                className="btn mt-4 text-white"
                style={{ backgroundColor: "#463aa1", height: "40px" }}
                onClick={(e) =>
                  dispatch(
                    addItem({
                      image: productData.attributes.image,
                      companyName: productData.attributes.company,
                      price: productData.attributes.price,
                      title: productData.attributes.title,
                      color: selectedColorIndex,
                      quantity: selectedQuantity,
                    })
                  )
                }
              >
                Add To Bag
              </div>
          {/* </Link> */}
          </div>
        </div>
      </div>
      {/* Empty div for space */}
      <div style={{ height: "50px" }}></div>
    </section>
  );
}

export default SingleProduct;

Cart.js

import { useSelector } from "react-redux";
function Cart() {
  const items = useSelector((state) => state);

  console.log("Items : ", items);
  // render code
}

App.js

import "./App.css";
import { RouterProvider, createBrowserRouter } from "react-router-dom";
import {
  About,
  Cart,
  Checkout,
  Error,
  HomeLayout,
  Landing,
  Login,
  Orders,
  Products,
  Register,
  SingleProduct,
} from "./pages";

const router = createBrowserRouter([
  {
    path: "/",
    element: <HomeLayout />,
    errorElement: <Error />,
    children: [
      {
        index: true,
        element: <Landing />,
      },
      {
        path: "products",
        element: <Products />,
      },
      {
        path: "products/:id",
        element: <SingleProduct />,
      },
      {
        path: "cart",
        element: <Cart />,
      },
      {
        path: "about",
        element: <About />,
      },
      {
        path: "checkout",
        element: <Checkout />,
      },
      {
        path: "orders",
        element: <Orders />,
      },
    ],
  },
  {
    path: "/login",
    element: <Login />,
    errorElement: <Error />,
  },
  {
    path: "/register",
    element: <Register />,
    errorElement: <Error />,
  },
]);
function App() {
  return <RouterProvider router={router} />;
}

export default App;
import React, { useEffect, useState } from "react";
import { BsSunFill, BsMoonFill, BsCart3 } from "react-icons/bs";
import { FaBarsStaggered } from "react-icons/fa6";
import { NavLink } from "react-router-dom";
import { NavLinks } from "./NavLinks";
import { useSelector } from "react-redux";

function Navbar() {
  const items = useSelector((state) => state);
  return (
    <nav className="navbar navbar-expand-sm  navbar-clr">
      <div className="container">
        <NavLink
          to="/"
          className=" navbar-brand btn btn-sm fs-6 d-none d-md-block text-white fs-6"
          style={{ background: "#016efe" }}
        >
          C
        </NavLink>
        <button
          class="navbar-toggler"
          type="button"
          data-bs-toggle="collapse"
          data-bs-target="#navbarNav"
          aria-controls="navbarNav"
          aria-expanded="false"
          aria-label="Toggle navigation"
        >
          <span class="navbar-toggler-icon">
            <FaBarsStaggered className="text-white" />
          </span>
        </button>

        <div class="collapse navbar-collapse" id="navbarNav">
          <ul class="navbar-nav mx-auto ">
            <li class="nav-item">
              <a
                class="nav-link active text-black  mx-3"
                aria-current="page"
                href="/"
              >
                Home
              </a>
            </li>
            <li class="nav-item">
              <a class="nav-link text-black mx-3" href="about">
                About
              </a>
            </li>
            <li class="nav-item">
              <a class="nav-link text-black mx-3" href="product">
                Product
              </a>
            </li>
            <li class="nav-item">
              <a
                class="nav-link text-black mx-3 "
                // style={{ background: "#02152c" }}
                href="/cart"
              >
                Cart
              </a>
            </li>
            <li class="nav-item">
              <a class="nav-link text-black mx-3" href="checkout">
                Checkout
              </a>
            </li>
            <li class="nav-item">
              <a class="nav-link text-black mx-3" href="orders">
                Orders
              </a>
            </li>
          </ul>
          {/* <NavLinks /> */}
        </div>

        <NavLink to="/cart" class="position-relative">
          <div>
            <BsCart3
              style={{ height: "30px", width: "30px" }}
              className="text-black"
            />
            <span
              class="position-absolute top-5 start-90 translate-middle badge rounded-pill"
              style={{ background: "#016efe" }}
            >
              {items.cart1.length}
            </span>
          </div>
        </NavLink>
      </div>
    </nav>
  );
}

export default Navbar;

问题现象

通过Redux DevTools调试时,发现购物车中有2件商品(符合预期),但在Cart.js中打印控制台时却看不到这些商品。

观察发现:如果点击“Add To Bag”后立即通过Link标签跳转到购物车页面,能看到商品;但单独进入购物车页面时,就会出现上述问题。


问题原因及解决方法

核心问题:路由跳转方式错误

Navbar中使用了原生<a>标签跳转路由,比如:

<a class="nav-link text-black mx-3 " href="/cart">Cart</a>

原生<a>标签会触发页面完全刷新,导致Redux store中的数据全部丢失,所以单独从Navbar进入购物车页面时看不到之前添加的商品。而点击“Add To Bag”后用Link跳转是单页应用的无刷新跳转,store数据得以保留,所以能看到商品。

解决步骤

  1. 替换所有原生<a>标签为React Router的<NavLink>或<Link>
    修改Navbar中的导航链接,示例如下:

    <li class="nav-item">
      <NavLink class="nav-link active text-black mx-3" to="/">
        Home
      </NavLink>
    </li>
    <li class="nav-item">
      <NavLink class="nav-link text-black mx-3" to="/about">
        About
      </NavLink>
    </li>
    <li class="nav-item">
      <NavLink class="nav-link text-black mx-3" to="/products">
        Product
      </NavLink>
    </li>
    <li class="nav-item">
      <NavLink class="nav-link text-black mx-3" to="/cart">
        Cart
      </NavLink>
    </li>
    <li class="nav-item">
      <NavLink class="nav-link text-black mx-3" to="/checkout">
        Checkout
      </NavLink>
    </li>
    <li class="nav-item">
      <NavLink class="nav-link text-black mx-3" to="/orders">
        Orders
      </NavLink>
    </li>
    

    确保已经导入NavLink:import { NavLink } from "react-router-dom";

  2. 优化Cart组件的state选择器
    当前Cart.js中直接取整个state,应精准获取cart1切片的数据:

    const items = useSelector((state) => state.cart1);
    

    这样控制台打印的就是购物车的商品数组,方便后续渲染。

  3. 可选:添加持久化存储(防止页面刷新丢失数据)
    如果希望页面刷新后购物车数据依然保留,可以使用redux-persist库:

    • 安装依赖:npm install redux-persist
    • 修改Store.js:
      import { configureStore } from "@reduxjs/toolkit";
      import { persistStore, persistReducer } from 'redux-persist';
      import storage from 'redux-persist/lib/storage'; // 默认localStorage
      import cartReducer from "./Slices/CartSlices";
      
      const persistConfig = {
        key: 'root',
        storage,
      };
      
      const persistedReducer = persistReducer(persistConfig, cartReducer);
      
      export const store = configureStore({
        reducer: {
          cart1: persistedReducer,
        },
      });
      
      export const persistor = persistStore(store);
      
    • 修改Index.js,添加PersistGate:
      import { Provider } from "react-redux";
      import { store, persistor } from "./Redux/Store";
      import { PersistGate } from 'redux-persist/integration/react';
      const root = ReactDOM.createRoot(document.getElementById("root"));
      root.render(
        <>
          <Provider store={store}>
            <PersistGate loading={null} persistor={persistor}>
              <App />
            </PersistGate>
          </Provider>
        </>
      );
      

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 14:20:56