如何在Next.js中实现购物车/结账页面的无刷新更新?
无刷新更新Saleor购物车状态的实现方案
针对你用Saleor无头后端+Next.js starter遇到的购物车刷新闪烁问题,核心思路是用客户端缓存/状态管理替代页面刷新,下面是两种可落地的实现方式:
1. 用SWR管理购物车数据(推荐,适配Next.js生态)
Saleor官方starter大多集成了SWR,直接基于它改造就能快速实现无刷新更新:
封装购物车请求Hook
先写一个复用的Hook来获取和更新购物车数据:
import useSWR from 'swr'; import { getCheckout } from '../lib/api'; // 你项目里调用Saleor API获取Checkout的函数 import { getCookie } from '../lib/cookies'; // 自定义的cookie读取工具 export function useCheckout() { // 用SWR缓存购物车数据,key设为固定值标识购物车请求 const { data: checkout, mutate } = useSWR('checkout-data', async () => { const checkoutId = getCookie('checkoutId'); if (!checkoutId) return null; return await getCheckout(checkoutId); }); return { checkout, refreshCheckout: mutate }; }
这里的mutate是SWR提供的手动更新缓存方法,调用后会重新拉取最新的Checkout数据,且只更新依赖该数据的组件。
修改AddToCart组件
在add-to-cart.tsx里,完成添加商品的API调用后,直接触发缓存更新:
import { useCheckout } from '../hooks/useCheckout'; import { addItemToCheckout } from '../lib/api'; // 调用Saleor API添加商品的函数 export default function AddToCart({ variantId }) { const { refreshCheckout } = useCheckout(); const handleAdd = async () => { const checkoutId = getCookie('checkoutId'); // 调用Saleor API添加商品到购物车 await addItemToCheckout(checkoutId, variantId, 1); // 手动更新购物车缓存,触发组件重渲染 await refreshCheckout(); }; return <button onClick={handleAdd}>加入购物车</button>; }
修改Cart组件渲染逻辑
在cart.tsx里,直接用Hook返回的checkout数据渲染即可,不用再依赖服务器端渲染的静态数据:
import { useCheckout } from '../hooks/useCheckout'; export default function Cart() { const { checkout } = useCheckout(); if (!checkout) return <div>购物车为空</div>; return ( <div className="cart-container"> <h3>我的购物车</h3> {checkout.lines.map(line => ( <div key={line.id} className="cart-item"> <span>{line.product.name}</span> <span>数量: {line.quantity}</span> <span>小计: {line.totalPrice.gross.amount} {line.totalPrice.gross.currency}</span> </div> ))} <div className="cart-total"> 总计: {checkout.totalPrice.gross.amount} {checkout.totalPrice.gross.currency} </div> </div> ); }
2. 用React Context自定义状态管理
如果不想用SWR,也可以自己写Context来维护购物车状态:
创建Checkout Context
// context/CheckoutContext.tsx import { createContext, useContext, useState, useEffect } from 'react'; import { getCheckout } from '../lib/api'; import { getCookie } from '../lib/cookies'; const CheckoutContext = createContext({ checkout: null, refreshCheckout: async () => {} }); export function CheckoutProvider({ children }) { const [checkout, setCheckout] = useState(null); // 初始化时加载购物车数据 useEffect(() => { const fetchInitCheckout = async () => { const checkoutId = getCookie('checkoutId'); if (checkoutId) { const data = await getCheckout(checkoutId); setCheckout(data); } }; fetchInitCheckout(); }, []); // 手动刷新购物车的方法 const refreshCheckout = async () => { const checkoutId = getCookie('checkoutId'); if (checkoutId) { const data = await getCheckout(checkoutId); setCheckout(data); } }; return ( <CheckoutContext.Provider value={{ checkout, refreshCheckout }}> {children} </CheckoutContext.Provider> ); } // 自定义Hook方便组件调用 export const useCheckout = () => useContext(CheckoutContext);
记得在_app.tsx里用CheckoutProvider包裹所有页面组件:
import { CheckoutProvider } from '../context/CheckoutContext'; export default function App({ Component, pageProps }) { return ( <CheckoutProvider> <Component {...pageProps} /> </CheckoutProvider> ); }
改造AddToCart和Cart组件
和SWR的方式类似,在add-to-cart.tsx里调用API后触发refreshCheckout,cart.tsx直接用Context里的checkout数据渲染即可,代码逻辑和上面SWR版本的组件一致,只是换了Hook来源。
关键注意点
- 确保你的API函数(比如
addItemToCheckout、getCheckout)能正确和Saleor的GraphQL API交互,添加/删除商品后,getCheckout能拿到最新的购物车状态。 - 删除商品的逻辑和添加完全一致:调用删除API后,触发
refreshCheckout或mutate即可。 - 如果用SWR,可以配置
revalidateOnFocus: true,确保用户切回页面时自动同步最新购物车状态。
内容的提问来源于stack exchange,提问作者Jonathan Franzen
相关产品推荐
相关产品推荐

