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

如何正确同步Next.js组件状态?实现购物车数量实时更新

解决React中购物车数量实时更新且避免全局use client的方案

不用把整个应用套进Context里,下面几个方案只让需要交互的组件设为客户端组件,其他部分保持服务端渲染:

方案1:自定义DOM事件

核心思路是用浏览器的自定义事件机制,让两个客户端组件直接通信,无需全局状态包裹。

购物车组件(use client)

'use client';

import { useEffect, useState } from 'react';

function CartCounter() {
  const [count, setCount] = useState(() => {
    // 初始化从localStorage读取商品数量
    const cart = localStorage.getItem('cart');
    return cart ? JSON.parse(cart).length : 0;
  });

  useEffect(() => {
    // 监听自定义更新事件
    const handleCartUpdate = () => {
      const cart = localStorage.getItem('cart');
      setCount(cart ? JSON.parse(cart).length : 0);
    };

    window.addEventListener('cartUpdated', handleCartUpdate);
    return () => window.removeEventListener('cartUpdated', handleCartUpdate);
  }, []);

  return <span>购物车:{count}</span>;
}

export default CartCounter;

商品卡片组件(use client)

'use client';

function ProductCard({ product }) {
  const addToCart = () => {
    // 更新localStorage中的购物车数据
    const existingCart = JSON.parse(localStorage.getItem('cart') || '[]');
    existingCart.push(product);
    localStorage.setItem('cart', JSON.stringify(existingCart));
    
    // 派发自定义事件通知购物车组件更新
    window.dispatchEvent(new CustomEvent('cartUpdated'));
  };

  return (
    <div className="product-card">
      <h3>{product.name}</h3>
      <button onClick={addToCart}>加入购物车</button>
    </div>
  );
}

export default ProductCard;

方案2:用React Query/SWR做数据同步

借助数据缓存库,把购物车数量当成可订阅的数据,更新时自动触发组件重渲染。

封装购物车数据逻辑

// app/lib/cart.ts
export const getCartCount = () => {
  const cart = localStorage.getItem('cart');
  return cart ? JSON.parse(cart).length : 0;
};

export const addToCart = (product) => {
  const existingCart = JSON.parse(localStorage.getItem('cart') || '[]');
  existingCart.push(product);
  localStorage.setItem('cart', JSON.stringify(existingCart));
};

购物车组件(use client)

'use client';

import { useQuery } from '@tanstack/react-query';
import { getCartCount } from '../lib/cart';

function CartCounter() {
  const { data: count } = useQuery({
    queryKey: ['cartCount'],
    queryFn: getCartCount,
  });

  return <span>购物车:{count || 0}</span>;
}

export default CartCounter;

商品卡片组件(use client)

'use client';

import { useQueryClient } from '@tanstack/react-query';
import { addToCart } from '../lib/cart';

function ProductCard({ product }) {
  const queryClient = useQueryClient();

  const handleAddToCart = () => {
    addToCart(product);
    // 使购物车数量的缓存失效,触发组件重新获取数据
    queryClient.invalidateQueries({ queryKey: ['cartCount'] });
  };

  return (
    <div className="product-card">
      <h3>{product.name}</h3>
      <button onClick={handleAddToCart}>加入购物车</button>
    </div>
  );
}

export default ProductCard;

方案3:轻量状态管理库(Zustand)

Zustand不需要包裹整个应用,只有使用store的组件需要标记为客户端组件,轻量化且易用。

创建购物车store

// app/store/cartStore.ts
import { create } from 'zustand';

interface CartStore {
  count: number;
  addItem: (product) => void;
}

export const useCartStore = create<CartStore>((set) => ({
  count: (() => {
    const cart = localStorage.getItem('cart');
    return cart ? JSON.parse(cart).length : 0;
  })(),
  addItem: (product) => {
    const existingCart = JSON.parse(localStorage.getItem('cart') || '[]');
    existingCart.push(product);
    localStorage.setItem('cart', JSON.stringify(existingCart));
    set({ count: existingCart.length });
  },
}));

购物车组件(use client)

'use client';

import { useCartStore } from '../store/cartStore';

function CartCounter() {
  const count = useCartStore((state) => state.count);
  return <span>购物车:{count}</span>;
}

export default CartCounter;

商品卡片组件(use client)

'use client';

import { useCartStore } from '../store/cartStore';

function ProductCard({ product }) {
  const addItem = useCartStore((state) => state.addItem);

  return (
    <div className="product-card">
      <h3>{product.name}</h3>
      <button onClick={() => addItem(product)}>加入购物车</button>
    </div>
  );
}

export default ProductCard;

这三个方案都只需要把商品卡片和购物车计数器标记为use client,应用的其他部分完全可以保持服务端渲染,无需全局包裹Context。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 07:03:26