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

React项目CartPage组件异步Thunk动态渲染异常问题

React购物车页面无法即时渲染新增商品问题

问题现象

  • 新增商品到购物车后,CartPage组件无法自动更新显示新商品,需手动刷新页面才能看到最新内容
  • 通过控制台日志确认Redux状态已正确更新,但组件渲染未同步变化
  • 添加商品操作执行成功,但跳转至购物车页面时页面呈空白状态,仅刷新后可见更新后的购物车内容

相关代码

cartSlice.ts

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
import api from '../../api'
import { AxiosError } from 'axios'
import { CartState } from '../../types/types'

const initialState: CartState = {
  cartItems: [],
  totalItems: 0,
  totalPrice: 0,
  error: null,
  isLoading: false,
  cartLength: 0
}
//** Add To Cart Thunks */
export const addToCartThunk = createAsyncThunk(
  'cart/addToCart',
  async (
    { productId, quantity, token }: { productId: string; quantity: number; token: string | null },
    { rejectWithValue }
  ) => {
    try {
      const response = await api.post(
        '/api/cart',
        { productId, quantity },
        {
          headers: {
            Authorization: `Bearer ${token}`
          }
        }
      )
      return response.data
    } catch (error) {
      if (error instanceof AxiosError) {
        return rejectWithValue(error.response?.data)
      }
    }
  }
)

//** Get Cart Items */
export const getCartItemsThunk = createAsyncThunk(
  'cart/getCartItems',
  async (token: string | null, { rejectWithValue }) => {
    try {
      const response = await api.get('/api/cart', {
        headers: {
          Authorization: `Bearer ${token}`
        }
      })

      return response.data
    } catch (error) {
      if (error instanceof AxiosError) {
        return rejectWithValue(error.response?.data)
      }
    }
  }
)

export const cartSlice = createSlice({
  name: 'cart',
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    //** Add To Cart Reducers */
    builder
      .addCase(addToCartThunk.pending, (state) => {
        state.isLoading = true
      })
      .addCase(addToCartThunk.fulfilled, (state, action) => {
        state.isLoading = false
        state.cartItems = action.payload.cartItems
        state.totalItems = action.payload.totalItems
        state.totalPrice = action.payload.totalPrice
      })
      .addCase(addToCartThunk.rejected, (state, action) => {
        const errorMsg = action.payload
        if (typeof errorMsg === 'string') {
          state.error = errorMsg
        }
        state.isLoading = false
        return state
      })

      //** Get Cart Items Reducers */
      .addCase(getCartItemsThunk.pending, (state) => {
        state.isLoading = true
      })
      .addCase(getCartItemsThunk.fulfilled, (state, action) => {
        state.isLoading = false
        state.cartItems = action.payload.cartItems
        state.totalItems = action.payload.totalItems
        state.totalPrice = action.payload.totalPrice
      })
      .addCase(getCartItemsThunk.rejected, (state, action) => {
        const errorMsg = action.payload
        if (typeof errorMsg === 'string') {
          state.error = errorMsg
        }
        state.isLoading = false
        return state
      })
  }
})

export default cartSlice.reducer

CartPage.tsx

import React, { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link, useNavigate } from 'react-router-dom';
import { toast } from 'react-toastify';
import swal from 'sweetalert';
import { getCartItemsThunk } from '../../redux/slices/cartSlice';
import { AppDispatch, RootState } from '../../redux/store';
import { getTokenFromStorage } from '../../utils/token';

export default function CartPage() {
  const { cartItems, totalPrice, isLoading } = useSelector((state: RootState) => state.cart);
  const dispatch = useDispatch<AppDispatch>();
  const navigate = useNavigate();
  
  useEffect(() => {
    const token = getTokenFromStorage();

    console.log("Token:", token);

    if (token) {
      dispatch(getCartItemsThunk(token))
    }
  }, []);

  console.log("Rendering CartPage with cartItems:", cartItems);


  //** Checkout Handler */
  const checkoutHandler = () => {
    swal({
      title: 'Checkout',
      text: 'Your order has been received successfully',
      icon: 'success',
    }).then((isOk) => {
      if (isOk) {
        navigate('/products');
      }
    });
  };

  if (isLoading) {
    return <div>Loading...</div>;
  }

  return (
    <div>
      {cartItems.length === 0 ? (
        <div>Your cart is empty.</div>
      ) : (
        <>
          {cartItems.map((product) => (
            <div key={product.product._id}>{product.product.name}</div>
          ))}
          <div>Total Price: ${totalPrice}</div>
        </>
      )}
    </div>
  );
}

控制台日志

Token: 获取到令牌
获取购物车项后:
Rendering CartPage with cartItems:
(5) [{…}, {…}, {…}, {…}, {…}]
0: {product: {…}, quantity: 4, _id: '657caf13399eb2072f240ffd'}
1: {product: {…}, quantity: 2, _id: '657cb8a1399eb2072f241199'}

报错信息

上述错误发生在<CartPage>组件中:

at CartPage (http://localhost:3000/src/pages/cart/CartPage.tsx?t=1702676943521:28:7)
at RenderedRoute (http://localhost:3000/node_modules/.vite/deps/react-router-dom.js?v=ddbf63b5:3405:5)

问题分析与修复方案

  1. 组件数据更新时机问题
    当前CartPage仅在组件首次挂载时拉取购物车数据,跳转至购物车页面时不会重新触发请求,导致显示旧状态。

    • 修复:监听路由变化,每次进入购物车页面时重新拉取数据
      import { useLocation } from 'react-router-dom';
      
      // 在组件内部添加
      const location = useLocation();
      
      useEffect(() => {
        const token = getTokenFromStorage();
        if (token) {
          dispatch(getCartItemsThunk(token))
        }
      }, [location.pathname]); // 监听路由路径变化
      
  2. 空白页面原因排查
    报错指向cartItems.map行,大概率是新增商品返回的结构中product.product为undefined,导致渲染报错。

    • 修复:添加空值安全检查
      {cartItems.map((item) => (
        <div key={item._id}>{item.product?.name || 'Unnamed Product'}</div>
      ))}
      
    • 额外验证:确认addToCartThunk与getCartItemsThunk返回的cartItems结构完全一致,避免字段差异导致渲染失败。
  3. Redux状态同步问题
    若Redux状态已更新但组件未渲染,可能是useSelector的浅比较失效:

    • 修复:使用shallowEqual强制浅比较状态
      import { shallowEqual } from 'react-redux';
      
      const { cartItems, totalPrice, isLoading } = useSelector((state: RootState) => state.cart, shallowEqual);
      

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 02:35:02