@@ -6,8 +6,8 @@ import React from 'react';
66import PropTypes from 'prop-types' ;
77
88import { isEqual } from '../utils/isEqual' ;
9- import { usePrevious } from '../utils/usePrevious ' ;
10- import { isStripe , isPromise } from '../utils/guards' ;
9+ import { usePromiseResolver } from '../utils/usePromiseResolver ' ;
10+ import { isStripe } from '../utils/guards' ;
1111
1212const INVALID_STRIPE_ERROR =
1313 'Invalid prop `stripe` supplied to `Elements`. We recommend using the `loadStripe` utility from `@stripe/stripe-js`. See https://stripe.com/docs/stripe-js/react#elements-props-stripe for details.' ;
@@ -23,28 +23,6 @@ const validateStripe = (maybeStripe: unknown): null | stripeJs.Stripe => {
2323 throw new Error ( INVALID_STRIPE_ERROR ) ;
2424} ;
2525
26- type ParsedStripeProp =
27- | { tag : 'empty' }
28- | { tag : 'sync' ; stripe : stripeJs . Stripe }
29- | { tag : 'async' ; stripePromise : Promise < stripeJs . Stripe | null > } ;
30-
31- const parseStripeProp = ( raw : unknown ) : ParsedStripeProp => {
32- if ( isPromise ( raw ) ) {
33- return {
34- tag : 'async' ,
35- stripePromise : Promise . resolve ( raw ) . then ( validateStripe ) ,
36- } ;
37- }
38-
39- const stripe = validateStripe ( raw ) ;
40-
41- if ( stripe === null ) {
42- return { tag : 'empty' } ;
43- }
44-
45- return { tag : 'sync' , stripe} ;
46- } ;
47-
4826interface ElementsContextValue {
4927 elements : stripeJs . StripeElements | null ;
5028 stripe : stripeJs . Stripe | null ;
@@ -66,6 +44,14 @@ export const parseElementsContext = (
6644 return ctx ;
6745} ;
6846
47+ const createElementsContext = ( stripe : stripeJs . Stripe | null , options ?: stripeJs . StripeElementsOptions ) => {
48+ const elements = stripe ? stripe . elements ( options ) : null
49+ return {
50+ stripe,
51+ elements
52+ }
53+ }
54+
6955interface ElementsProps {
7056 /**
7157 * A [Stripe object](https://stripe.com/docs/js/initializing) or a `Promise` resolving to a `Stripe` object.
@@ -101,74 +87,49 @@ interface PrivateElementsProps {
10187 */
10288export const Elements : FunctionComponent < ElementsProps > = ( {
10389 stripe : rawStripeProp ,
104- options,
90+ options : optionsProp ,
10591 children,
10692} : PrivateElementsProps ) => {
107- const final = React . useRef ( false ) ;
108- const isMounted = React . useRef ( true ) ;
109- const parsed = React . useMemo ( ( ) => parseStripeProp ( rawStripeProp ) , [
110- rawStripeProp ,
111- ] ) ;
112- const [ ctx , setContext ] = React . useState < ElementsContextValue > ( ( ) => ( {
113- stripe : null ,
114- elements : null ,
115- } ) ) ;
116-
117- const prevStripe = usePrevious ( rawStripeProp ) ;
118- const prevOptions = usePrevious ( options ) ;
119- if ( prevStripe !== null ) {
120- if ( prevStripe !== rawStripeProp ) {
93+ const [ inputs , setInputs ] = React . useState ( { rawStripe : rawStripeProp , options : optionsProp } )
94+ const { rawStripe, options } = inputs
95+ React . useEffect ( ( ) => {
96+ const hasRawStripeChanged = rawStripe !== rawStripeProp
97+ const hasOptionsChanged = ! isEqual ( options , optionsProp )
98+ const canUpdate = rawStripe === null
99+
100+ if ( hasRawStripeChanged && ! canUpdate ) {
121101 console . warn (
122102 'Unsupported prop change on Elements: You cannot change the `stripe` prop after setting it.'
123103 ) ;
124104 }
125- if ( ! isEqual ( options , prevOptions ) ) {
105+
106+ if ( hasOptionsChanged && ! canUpdate ) {
126107 console . warn (
127108 'Unsupported prop change on Elements: You cannot change the `options` prop after setting the `stripe` prop.'
128109 ) ;
129110 }
130- }
131111
132- if ( ! final . current ) {
133- if ( parsed . tag === 'sync' ) {
134- final . current = true ;
135- setContext ( {
136- stripe : parsed . stripe ,
137- elements : parsed . stripe . elements ( options ) ,
138- } ) ;
139- }
112+ if ( hasRawStripeChanged && canUpdate ) setInputs ( { rawStripe : rawStripeProp , options : optionsProp } )
113+ } , [ rawStripe , options , rawStripeProp , optionsProp ] )
140114
141- if ( parsed . tag === 'async' ) {
142- final . current = true ;
143- parsed . stripePromise . then ( ( stripe ) => {
144- if ( stripe && isMounted . current ) {
145- // Only update Elements context if the component is still mounted
146- // and stripe is not null. We allow stripe to be null to make
147- // handling SSR easier.
148- setContext ( {
149- stripe,
150- elements : stripe . elements ( options ) ,
151- } ) ;
152- }
153- } ) ;
154- }
155- }
115+ const maybeStripe = usePromiseResolver ( rawStripe )
116+ const stripe = validateStripe ( maybeStripe )
117+ const [ ctx , setContext ] = React . useState < ElementsContextValue > ( ( ) => createElementsContext ( null ) ) ;
156118
157- React . useEffect ( ( ) => {
158- return ( ) : void => {
159- isMounted . current = false ;
160- } ;
161- } , [ ] ) ;
162119
163120 React . useEffect ( ( ) => {
164- const anyStripe : any = ctx . stripe ;
121+ const anyStripe : any = stripe ;
165122
166123 if ( ! anyStripe || ! anyStripe . _registerWrapper ) {
167124 return ;
168125 }
169126
170127 anyStripe . _registerWrapper ( { name : 'react-stripe-js' , version : _VERSION } ) ;
171- } , [ ctx . stripe ] ) ;
128+ } , [ stripe ] ) ;
129+
130+ React . useEffect ( ( ) => {
131+ if ( stripe ) setContext ( createElementsContext ( stripe , options ) )
132+ } , [ stripe , options ] )
172133
173134 return (
174135 < ElementsContext . Provider value = { ctx } > { children } </ ElementsContext . Provider >
0 commit comments