如何正确同步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
相关产品推荐
相关产品推荐

