React Stripe Elements & SSR - Webpack Error: Window is Not Defined
React Stripe Elements & SSR - Webpack Error: Window is Not Defined
React Stripe Elements 在开发中运行良好,但通过 Netlify 实时部署会在 Provider.js react stripe 元素节点模块文件中抛出 'Webpack: Window is undefined'。
根据其他一些建议,我尝试了 ComponentDidMount 方法并使用以下方法编辑 Provider.js:
if (typeof window !== 'undefined') {
let iInnerHeight = window.innerHeight;
}
两者仍然会导致部署失败。
此外,我尝试在 StripeProvider 组件中设置 stripe 或 apiKey,设置 stripe 会抛出需要 Stripe 对象的错误,例如Stripe(...) --> 当用这个 get Stripe is not defined 和 apiKey throws window undefined 错误切换时。
这是我的 gatsby-ssr.js 文件:
import React from 'react'
import { ShopkitProvider } from './src/shopkit'
import { StripeProvider, Elements } from 'react-stripe-elements'
import Layout from './src/components/Layout'
export const wrapRootElement = ({ element }) => {
return (
<StripeProvider apiKey={process.env.GATSBY_STRIPE_PUBLISHABLE_KEY}>
<ShopkitProvider clientId{process.env.GATSBY_MOLTIN_CLIENT_ID}>
<Elements>{element}</Elements>
</ShopkitProvider>
</StripeProvider>
)
}
export const wrapPageElement = ({ element, props }) => {
return <Layout {...props}>{element}</Layout>
}
一切都按预期进行开发,但 SSR 存在 window Webpack 未定义的问题。我还在 Netlify 以及 .env 文件中设置了 env 变量
问题是在 StripeProvider
内检查 window
中的 Stripe 对象。这意味着您不能在 wrapRootElement
中直接使用它。简单的解决办法就是在gatsby-ssr.js
里不用StripeProvider
,只在gatsby-browser.js
.
里用
但是,由于您使用多个服务提供商包装根,而且如果您像这样异步加载 Stripe:
// somewhere else vvvvv
<script id="stripe-js" src="https://js.stripe.com/v3/" async />
您不妨制作一个可以在 gatsby-ssr
和 gatsby-browser
中使用的通用包装器,这样更容易维护。
我通过为 StripeProvider
创建一个包装器来做到这一点,其中 Stripe
是根据 window
和 window.Stripe
的可用性手动启动的。然后 stripe
实例作为道具传递给 StripeProvider
而不是 api 键。
// pseudo
const StripeWrapper = ({ children }) => {
let stripe,
if (no window) stripe = null
if (window.Stripe) stripe = window.Stripe(...)
else {
stripeLoadingScript.onload = () => window.Stripe(...)
}
return (
<StripeProvider stripe={stripe}>
{children}
<StripeProvider>
)
}
此逻辑应放在 componentDidMount
或 useEffect
挂钩中。这是一个带钩子的例子:
import React, { useState, useEffect } from 'react'
import { StripeProvider } from 'react-stripe-elements'
const StripeWrapper = ({ children }) => {
const [ stripe, setStripe ] = useState(null)
useEffect(() => {
// for SSR
if (typeof window == 'undefined') return
// for browser
if (window.Stripe) {
setStripe(window.Stripe(process.env.STRIPE_PUBLIC_KEY))
} else {
const stripeScript = document.querySelector('#stripe-js')
stripeScript.onload = () => {
setStripe(window.Stripe(process.env.STRIPE_PUBLIC_KEY))
}
}
}, []) // <-- passing in an empty array since I only want to run this hook once
return (
<StripeProvider stripe={stripe}>
{children}
</StripeProvider>
)
}
// export a `wrapWithStripe` function that can used
// in both gatsby-ssr.js and gatsby-browser.js
const wrapWithStripe = ({ element }) => (
<StripeWrapper>
<OtherServiceProvider>
{element}
</OtherServiceProvider>
</StripeWrapper>
)
通过在 gatsby-config.js
中将异步设置为 true
{
resolve: `gatsby-plugin-stripe`,
options: {
async: true
}
}
可以简化上面的代码。
const Stripe = props => {
const [stripe, setStripe] = useState(null);
useEffect(() => {
(async () => {
const obj = await window.Stripe(process.env.STRIPE_PUBLIC_KEY);
setStripe(obj);
})();
}, []);
return (
<>
<StripeProvider stripe={stripe}>
{children}
</StripeProvider>
</>
);
};
React Stripe Elements 在开发中运行良好,但通过 Netlify 实时部署会在 Provider.js react stripe 元素节点模块文件中抛出 'Webpack: Window is undefined'。
根据其他一些建议,我尝试了 ComponentDidMount 方法并使用以下方法编辑 Provider.js:
if (typeof window !== 'undefined') {
let iInnerHeight = window.innerHeight;
}
两者仍然会导致部署失败。
此外,我尝试在 StripeProvider 组件中设置 stripe 或 apiKey,设置 stripe 会抛出需要 Stripe 对象的错误,例如Stripe(...) --> 当用这个 get Stripe is not defined 和 apiKey throws window undefined 错误切换时。
这是我的 gatsby-ssr.js 文件:
import React from 'react'
import { ShopkitProvider } from './src/shopkit'
import { StripeProvider, Elements } from 'react-stripe-elements'
import Layout from './src/components/Layout'
export const wrapRootElement = ({ element }) => {
return (
<StripeProvider apiKey={process.env.GATSBY_STRIPE_PUBLISHABLE_KEY}>
<ShopkitProvider clientId{process.env.GATSBY_MOLTIN_CLIENT_ID}>
<Elements>{element}</Elements>
</ShopkitProvider>
</StripeProvider>
)
}
export const wrapPageElement = ({ element, props }) => {
return <Layout {...props}>{element}</Layout>
}
一切都按预期进行开发,但 SSR 存在 window Webpack 未定义的问题。我还在 Netlify 以及 .env 文件中设置了 env 变量
问题是在 StripeProvider
内检查 window
中的 Stripe 对象。这意味着您不能在 wrapRootElement
中直接使用它。简单的解决办法就是在gatsby-ssr.js
里不用StripeProvider
,只在gatsby-browser.js
.
但是,由于您使用多个服务提供商包装根,而且如果您像这样异步加载 Stripe:
// somewhere else vvvvv
<script id="stripe-js" src="https://js.stripe.com/v3/" async />
您不妨制作一个可以在 gatsby-ssr
和 gatsby-browser
中使用的通用包装器,这样更容易维护。
我通过为 StripeProvider
创建一个包装器来做到这一点,其中 Stripe
是根据 window
和 window.Stripe
的可用性手动启动的。然后 stripe
实例作为道具传递给 StripeProvider
而不是 api 键。
// pseudo
const StripeWrapper = ({ children }) => {
let stripe,
if (no window) stripe = null
if (window.Stripe) stripe = window.Stripe(...)
else {
stripeLoadingScript.onload = () => window.Stripe(...)
}
return (
<StripeProvider stripe={stripe}>
{children}
<StripeProvider>
)
}
此逻辑应放在 componentDidMount
或 useEffect
挂钩中。这是一个带钩子的例子:
import React, { useState, useEffect } from 'react'
import { StripeProvider } from 'react-stripe-elements'
const StripeWrapper = ({ children }) => {
const [ stripe, setStripe ] = useState(null)
useEffect(() => {
// for SSR
if (typeof window == 'undefined') return
// for browser
if (window.Stripe) {
setStripe(window.Stripe(process.env.STRIPE_PUBLIC_KEY))
} else {
const stripeScript = document.querySelector('#stripe-js')
stripeScript.onload = () => {
setStripe(window.Stripe(process.env.STRIPE_PUBLIC_KEY))
}
}
}, []) // <-- passing in an empty array since I only want to run this hook once
return (
<StripeProvider stripe={stripe}>
{children}
</StripeProvider>
)
}
// export a `wrapWithStripe` function that can used
// in both gatsby-ssr.js and gatsby-browser.js
const wrapWithStripe = ({ element }) => (
<StripeWrapper>
<OtherServiceProvider>
{element}
</OtherServiceProvider>
</StripeWrapper>
)
通过在 gatsby-config.js
中将异步设置为 true{
resolve: `gatsby-plugin-stripe`,
options: {
async: true
}
}
可以简化上面的代码。
const Stripe = props => {
const [stripe, setStripe] = useState(null);
useEffect(() => {
(async () => {
const obj = await window.Stripe(process.env.STRIPE_PUBLIC_KEY);
setStripe(obj);
})();
}, []);
return (
<>
<StripeProvider stripe={stripe}>
{children}
</StripeProvider>
</>
);
};