React Stripe Elements和SSR-Webpack错误:未定义窗口

时间:2019-06-25 19:43:12

标签: reactjs gatsby react-stripe-elements

React Stripe Elements在开发中工作正常,但通过Netlify实时部署会抛出Provider.js react stripe elements节点模块文件中的“ Webpack:未定义窗口”。

根据其他建议,我尝试了ComponentDidMount方法,并使用以下方法编辑Provider.js:

if (typeof window !== 'undefined') {
    let iInnerHeight = window.innerHeight;
}

两者仍然导致部署失败。

此外,我尝试在StripeProvider组件中设置stripe或apiKey,设置stripe throws错误需要Stripe对象,例如Stripe(...)->使用此get切换时,未定义Stripe,并且apiKey引发窗口未定义错误。

这是我的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呈现Webpack窗口未定义的问题。我还在Netlify和.env文件中设置了env变量

2 个答案:

答案 0 :(得分:1)

问题在于window中的StripeProvider中是否有Stripe对象的检查。这意味着您不能在wrapRootElement中原始使用它。简单的解决方案是不要在StripeProvider中使用gatsby-ssr.js,而只在gatsby-browser.js中使用它。


但是,由于要用多个服务提供程序包装根目录,并且还需要像这样异步加载Stripe:

// somewhere else                                      vvvvv
<script id="stripe-js" src="https://js.stripe.com/v3/" async />

您最好制作一个可以在gatsby-ssrgatsby-browser中使用的通用包装,以便于维护。

为此,我为StripeProvider创建了一个包装器,其中Stripe是根据windowwindow.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>
  )
}

此逻辑应放在componentDidMountuseEffect钩中。这是带有钩子的示例:

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>
)

答案 1 :(得分:1)

通过在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>
    </>
  );
};