@@ -6,8 +6,8 @@ import React from 'react';
6
6
import PropTypes from 'prop-types' ;
7
7
8
8
import { 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' ;
11
11
12
12
const INVALID_STRIPE_ERROR =
13
13
'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 => {
23
23
throw new Error ( INVALID_STRIPE_ERROR ) ;
24
24
} ;
25
25
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
-
48
26
interface ElementsContextValue {
49
27
elements : stripeJs . StripeElements | null ;
50
28
stripe : stripeJs . Stripe | null ;
@@ -66,6 +44,14 @@ export const parseElementsContext = (
66
44
return ctx ;
67
45
} ;
68
46
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
+
69
55
interface ElementsProps {
70
56
/**
71
57
* A [Stripe object](https://stripe.com/docs/js/initializing) or a `Promise` resolving to a `Stripe` object.
@@ -101,74 +87,49 @@ interface PrivateElementsProps {
101
87
*/
102
88
export const Elements : FunctionComponent < ElementsProps > = ( {
103
89
stripe : rawStripeProp ,
104
- options,
90
+ options : optionsProp ,
105
91
children,
106
92
} : 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 ) {
121
101
console . warn (
122
102
'Unsupported prop change on Elements: You cannot change the `stripe` prop after setting it.'
123
103
) ;
124
104
}
125
- if ( ! isEqual ( options , prevOptions ) ) {
105
+
106
+ if ( hasOptionsChanged && ! canUpdate ) {
126
107
console . warn (
127
108
'Unsupported prop change on Elements: You cannot change the `options` prop after setting the `stripe` prop.'
128
109
) ;
129
110
}
130
- }
131
111
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 ] )
140
114
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 ) ) ;
156
118
157
- React . useEffect ( ( ) => {
158
- return ( ) : void => {
159
- isMounted . current = false ;
160
- } ;
161
- } , [ ] ) ;
162
119
163
120
React . useEffect ( ( ) => {
164
- const anyStripe : any = ctx . stripe ;
121
+ const anyStripe : any = stripe ;
165
122
166
123
if ( ! anyStripe || ! anyStripe . _registerWrapper ) {
167
124
return ;
168
125
}
169
126
170
127
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 ] )
172
133
173
134
return (
174
135
< ElementsContext . Provider value = { ctx } > { children } </ ElementsContext . Provider >
0 commit comments