如何为react-stripe-elements创建useStripe钩子

And*_*i R 3 stripe-payments reactjs react-stripe-elements react-hooks

Stripe具有react-stripe-elements,提供injectStripeHOC。我们现在在2019年,而HOC不再酷了。Stripe似乎并不着急,我认为这是因为他们想支持React的较早版本,因此只关注最低的公分母解决方案。

我正在寻找一种stripe通过钩子获取的方法(而不是由提供的道具injectStripe)。

用法看起来像这样, const stripe = useStripe() 以便以后可以使用stripe.handleCardSetup和其他API方法

import { CardElement } from 'react-stripe-elements'

const CardForm = ({secret}) => {
  const stripe = useStripe()

  const handleSubmit = async () => {
    const { setupIntent, error } = await stripe.handleCardSetup(secret)
    // ...
  }

  return (
    <>
      <CardElement />
      <button onClick={handleSubmit} />
    </>
  )
}
Run Code Online (Sandbox Code Playgroud)

您如何定义useStripe现有API和组件的钩子? stripe应该与您使用injectStripe钩子时得到的相同

GH上的相关问题:API评论:Hooks&Beyond

And*_*i R 6

经过一堆反复的试验,我最终得出结论,这很简单。在这里是条纹窥视所采用的,而其他需要useStripe钩的则是

// StripeHookProvider.jsx

import React, { useContext, createContext } from 'react'
import { injectStripe } from 'react-stripe-elements'

const Context = createContext()

export const useStripe = () => useContext(Context)

const HookProvider = ({ children, stripe }) => {
  return <Context.Provider value={stripe}>{children}</Context.Provider>
}

export default injectStripe(HookProvider)
Run Code Online (Sandbox Code Playgroud)

StripeHookProvider像这样导入并添加到您的组件层次结构中。注意:因为它依赖injectStripe,所以它必须是<Elements>

    <StripeProvider {...{ stripe }}>
      <Elements>
        <StripeHookProvider>
          <MyForm />
        </StripeHookProvider>
      </Elements>
    </StripeProvider>
Run Code Online (Sandbox Code Playgroud)

然后在MyForm中使用它像这样

    <StripeProvider {...{ stripe }}>
      <Elements>
        <StripeHookProvider>
          <MyForm />
        </StripeHookProvider>
      </Elements>
    </StripeProvider>
Run Code Online (Sandbox Code Playgroud)

这样,您只需要injectStripe在一个地方做,就可以将它整齐地藏在视线之外。从该代码中可以看出,这不是火箭科学,对于条纹偷看者来说,将其添加到他们的代码中应该没有问题,<StripeHookProvider>因为它们stripe在上下文中引用了对象,因此希望完全消除。

我尝试了很多方法,但这是唯一可行的方法。我没有深入研究Stripe的JS代码来了解会发生什么,但是stripeinjectStripe只能从中得到使用。您通过设置的window.Stripe(apiKey)那个是不一样的,它将无法正常工作,因为它无法<Elements>正确观察,因此它不知道表单的状态。

也许,injectStripe必须对孩子使用<Elements>的原因与Stripe团队花一些时间来构建此钩子的原因相同。

  • 可惜我不能投票一百次-第一次工作! (2认同)

cl0*_*k3r 2

我自己已经完成了,并且我已经为你准备了要点:)

useStripe钩子使用一个useScript钩子负责加载异步脚本,例如条带脚本。

要点如下:useScript.js

我已经简化了它,您可能也不需要区域设置。

这是使用脚本:

import React from 'react'

let cache = []
const isCached = src => cache.includes(src)

export const useScript = src => {
  const [state, setState] = React.useState({
    isLoaded: isCached(src),
    hasError: false,
  })

  React.useEffect(() => {
    if (!src || isCached(src)) {
      return
    }

    cache.push(src)

    const script = document.createElement('script')
    script.src = src
    script.async = true

    const onScriptLoad = () => {
      setState(s => ({...s, isLoaded: true, hasError: false}))
    }
    const onScriptError = () => {
      const index = cache.indexOf(src)
      if (index >= 0) {
        cache.splice(index, 1)
      }
      script.remove()
      setState(s => ({...s, hasError: true, isLoaded: true}))
    }

    script.addEventListener('load', onScriptLoad)
    script.addEventListener('error', onScriptError)

    document.body.appendChild(script)

    return () => {
      script.removeEventListener('load', onScriptLoad)
      script.removeEventListener('error', onScriptError)
    }
  }, [src])

  return state
}
Run Code Online (Sandbox Code Playgroud)

这是useStripe钩子:

import React from 'react'

import {useScript} from './useScript'

let cache = {}

export const useStripe = ({locale, stripeKey}) => {
  const {isLoaded, error} = useScript('https://js.stripe.com/v3/')
  const [stripe, setStripe] = React.useState(cache[locale])

  React.useEffect(() => {
    if (isLoaded && !error && !cache[locale]) {
      cache[locale] = window.Stripe(stripeKey, {locale})
      setStripe(cache[locale])
    }
  }, [isLoaded, error, locale])

  return {
    stripe,
    error,
  }
}
Run Code Online (Sandbox Code Playgroud)

这只是一个钩子,但如果您需要上下文 API,您可以将 useStripe 代码移至 Provider 内!