簡體   English   中英

React Stripe Elements & SSR - Webpack 錯誤:未定義窗口

[英]React Stripe Elements & SSR - Webpack Error: Window is Not Defined

React Stripe Elements 在開發中運行良好,但通過 Netlify 進行實時部署會在 Provider.js react Stripe Elements 節點模塊文件中拋出“Webpack: Window is undefined”。

根據其他一些建議,我嘗試了 ComponentDidMount 方法並使用以下內容編輯 Provider.js:

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

兩者仍然會導致部署失敗。

另外,我嘗試在 StripeProvider 組件中設置 stripe 或 apiKey,設置 Stripe 會引發錯誤,需要 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 變量

問題是在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-ssrgatsby-browser使用的通用包裝gatsby-browser以便更易於維護。

我通過創建一個包裝這樣做StripeProvider其中Stripe取決於可用性手動啟動windowwindow.Stripe 然后將stripe實例作為 prop 傳遞給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>
)

通過在 gatsby-config.js 中將 async 設置為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>
    </>
  );
};

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM