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

React/Next.js中PayPal按钮购物车改量后不更新问题

React PayPal Buttons 未同步购物车更新的问题解决

修改购物车商品数量后,本地存储和购物车状态已更新,但PayPalButtons的createOrder、onApprove回调始终使用旧的total和uniqueProducts值,导致结账金额错误、数据库写入的商品数量不正确。

问题根源

PayPalButtons组件初始化时,其回调函数会捕获组件首次渲染时的闭包变量。后续购物车更新触发组件重新渲染时,这些回调并未重新绑定,依然引用旧的total和uniqueProducts值。

解决方案

1. 使用useRef保存最新props值

通过useRef实时同步props的最新状态,确保回调函数能获取到当前最新的购物车数据:

'use client'

import { PayPalButtons, PayPalScriptProvider } from '@paypal/react-paypal-js'
import { createPaypalOrder, handlePaypalCheckout } from '../../_actions/paypal'
import { useRouter } from 'next/navigation'
import { useContext, useRef, useEffect } from 'react'
import { CartContext } from '@/app/_context/CartContext'
import { toast } from '../ui/use-toast'
import { useSession } from 'next-auth/react'

export default function PaypalButton({ total, uniqueProducts }) {
  const { data: session } = useSession()
  const { setShowCart } = useContext(CartContext)
  const router = useRouter()

  // 用ref保存最新的props值
  const latestTotal = useRef(total)
  const latestUniqueProducts = useRef(uniqueProducts)
  const latestSession = useRef(session)

  // 监听props变化,实时更新ref
  useEffect(() => {
    latestTotal.current = total
    latestUniqueProducts.current = uniqueProducts
    latestSession.current = session
  }, [total, uniqueProducts, session])

  const captureOrder = async (orderId) => {
    try {
      const res = await fetch('/api/paypal/capture', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ orderId: orderId }),
      })

      const orderCapture = await res.json()

      if (orderCapture.status == 200) {
        // 使用ref中的最新数据
        const orderId = await createPaypalOrder(
          latestUniqueProducts.current,
          orderCapture.response.result,
          latestTotal.current,
          latestSession.current?.user.email,
        )
        setShowCart(false)
        router.push(`/order/success?order_id=${orderId}`)
      }
    } catch (error) {
      toast({
        title: 'Invalid payment',
        variant: 'destructive',
      })
      console.log('🚀 ~ captureOrder ~ error:', error)
    }
  }

  const createOrderAction = async () => {
    try {
      // 使用ref中的最新数据
      console.log(latestTotal.current)
      console.log(latestUniqueProducts.current)

      const res = await fetch('/api/paypal', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ total: latestTotal.current }),
      })
      const order = await res.json()
      if (order.id) return order.id
      else {
        const errorDetail = order?.details?.[0]
        const errorMessage = errorDetail
          ? `${errorDetail.issue} ${errorDetail.description} (${order.debug_id})`
          : JSON.stringify(order)

        throw new Error(errorMessage)
      }
    } catch (error) {
      console.log('🚀 ~ createOrderAction ~ error:', error)
      console.error(error)
    }
  }

  return (
    <div>
      <PayPalScriptProvider
        options={{
          clientId: 'test',
          disableFunding: 'card',
          currency: 'EUR',
        }}
      >
        <PayPalButtons
          style={{
            color: 'gold',
            disableMaxWidth: 'true',
          }}
          createOrder={async (data, actions) => {
            const orderId = await createOrderAction()
            return orderId
          }}
          onApprove={async (data, actions) => {
            await captureOrder(data.orderID)
          }}
          onCancel={(data) => {}}
          onError={(err) => {
            console.log('🚀 ~ err:', err)
            console.error('PayPal Checkout onError', err)
          }}
        />
      </PayPalScriptProvider>
    </div>
  )
}

2. 添加key属性强制组件重新挂载

如果上述方法仍有问题,可以给PayPalButtons添加动态key,当购物车数据变化时强制组件重新初始化,确保回调使用最新的props:

<PayPalButtons
  key={`paypal-btn-${total}-${JSON.stringify(uniqueProducts)}`}
  // 其他属性保持不变
/>

额外修复点

  • 原代码中createOrder回调里的createOrderAction()未加await,会导致返回Promise而非实际的orderId,已修正。
  • 原代码错误引用了未定义的orderData,已改为order。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 13:33:19