开发正常但部署Vercel时出现usePayPalScriptReducer报错求助
问题原因及解决方案
核心原因
你遇到的usePayPalScriptReducer must be used within a PayPalScriptProvider错误,和getStaticPaths/getStaticProps的静态生成机制直接相关:
- 开发环境中,Next.js默认以客户端渲染为主,PayPal组件的代码在浏览器中执行,
PayPalScriptProvider能正常包裹PayPalButtons,钩子可以正常工作。 - 部署到Vercel时,
getStaticProps会触发服务器端预渲染,页面在构建阶段就会被生成静态HTML。此时PayPalButtons内部的usePayPalScriptReducer钩子会在服务器端执行,但服务器端没有PayPalScriptProvider的上下文(Provider仅在客户端 hydration 时才会挂载),因此抛出错误。
解决方案
方案1:标记组件为客户端组件(Next.js 13+)
在你的Join组件顶部添加'use client'指令,强制该组件仅在客户端执行,避免服务器端预渲染时触发PayPal的钩子:
'use client' // 新增这一行 import { PayPalScriptProvider, PayPalButtons } from '@paypal/react-paypal-js' import { getAllBootcampsSlugs, getBootcampDataBySlug, } from './../../../src/utils/bootcampsApi' // 后续组件代码保持不变 function Join() { function createOrder(data, actions) { return actions.order .create({ purchase_units: [ { amount: { value: 5, }, }, ], application_context: { shipping_preference: 'NO_SHIPPING', }, }) .then((orderID) => { return orderID }) } async function onApprove(data, actions) { // 你的支付确认逻辑 } function onError(data, actions) { // 你的错误处理逻辑 } return ( <div className="min-h-screen flex flex-col justify-center items-center"> <PayPalScriptProvider options={{ 'client-id': process.env.NEXT_PUBLIC_PAYPAL_CLIENT_ID, }}> <PayPalButtons style={{ color: 'black', label: 'pay', tagline: false, }} createOrder={createOrder} onApprove={onApprove} onError={onError} /> </PayPalScriptProvider> </div> ) } // ...getStaticPaths和getStaticProps保持不变
方案2:动态导入并禁用SSR(兼容旧版Next.js)
如果使用的是Next.js 12及以下版本,或者不想标记整个页面为客户端组件,可以将PayPal相关逻辑抽成独立组件,然后通过动态导入禁用服务器端渲染:
- 新建
PayPalCheckout.js组件:
import { PayPalScriptProvider, PayPalButtons } from '@paypal/react-paypal-js' export default function PayPalCheckout() { function createOrder(data, actions) { return actions.order .create({ purchase_units: [ { amount: { value: 5, }, }, ], application_context: { shipping_preference: 'NO_SHIPPING', }, }) .then((orderID) => { return orderID }) } async function onApprove(data, actions) { // 你的支付确认逻辑 } function onError(data, actions) { // 你的错误处理逻辑 } return ( <PayPalScriptProvider options={{ 'client-id': process.env.NEXT_PUBLIC_PAYPAL_CLIENT_ID, }}> <PayPalButtons style={{ color: 'black', label: 'pay', tagline: false, }} createOrder={createOrder} onApprove={onApprove} onError={onError} /> </PayPalScriptProvider> ) }
- 修改原
Join页面,动态导入该组件:
import dynamic from 'next/dynamic' import { getAllBootcampsSlugs, getBootcampDataBySlug, } from './../../../src/utils/bootcampsApi' // 动态导入并禁用SSR const PayPalCheckout = dynamic(() => import('./PayPalCheckout'), { ssr: false, }) function Join({ data }) { return ( <div className="min-h-screen flex flex-col justify-center items-center"> <PayPalCheckout /> </div> ) } export default Join Join.getLayout = (page) => page export async function getStaticPaths() { const paths = getAllBootcampsSlugs().map((bootcamp) => ({ params: { bootcamp, }, })) return { paths, fallback: false, } } export async function getStaticProps(context) { const { params } = context const data = getBootcampDataBySlug(params.bootcamp) return { props: { data: data, }, } }
验证
修改后重新部署到Vercel,静态生成过程中不会再执行PayPal组件的服务器端代码,客户端渲染时PayPalScriptProvider会正常提供上下文,错误即可解决。
内容的提问来源于stack exchange,提问作者Naveh Mevorach
相关产品推荐
相关产品推荐

