@@ -252,6 +252,11 @@ export interface BaseSignInProps {
252252 */
253253 onFlowChange ?: ( response : EmbeddedSignInFlowInitiateResponse | EmbeddedSignInFlowHandleResponse ) => void ;
254254
255+ /**
256+ * Flag to determine the component is ready to be rendered.
257+ */
258+ isLoading ?: boolean ;
259+
255260 /**
256261 * Function to initialize authentication flow.
257262 * @returns Promise resolving to the initial authentication response.
@@ -328,6 +333,7 @@ const BaseSignIn: FC<BaseSignInProps> = props => (
328333const BaseSignInContent : FC < BaseSignInProps > = ( {
329334 afterSignInUrl,
330335 onInitialize,
336+ isLoading : externalIsLoading ,
331337 onSubmit,
332338 onSuccess,
333339 onError,
@@ -343,15 +349,16 @@ const BaseSignInContent: FC<BaseSignInProps> = ({
343349 const { t} = useTranslation ( ) ;
344350 const { subtitle : flowSubtitle , title : flowTitle , messages : flowMessages } = useFlow ( ) ;
345351
346- const [ isLoading , setIsLoading ] = useState ( false ) ;
352+ const [ isSignInInitializationRequestLoading , setIsSignInInitializationRequestLoading ] = useState ( false ) ;
347353 const [ isInitialized , setIsInitialized ] = useState ( false ) ;
348354 const [ currentFlow , setCurrentFlow ] = useState < EmbeddedSignInFlowInitiateResponse | null > ( null ) ;
349355 const [ currentAuthenticator , setCurrentAuthenticator ] = useState < EmbeddedSignInFlowAuthenticator | null > ( null ) ;
350356 const [ error , setError ] = useState < string | null > ( null ) ;
351357 const [ messages , setMessages ] = useState < Array < { message : string ; type : string } > > ( [ ] ) ;
352358
353- // Ref to track if initialization has been attempted to prevent multiple calls
354- const initializationAttemptedRef = useRef ( false ) ;
359+ const isLoading = externalIsLoading || isSignInInitializationRequestLoading ;
360+
361+ const reRenderCheckRef = useRef ( false ) ;
355362
356363 const formFields : FormField [ ] =
357364 currentAuthenticator ?. metadata ?. params ?. map ( param => ( {
@@ -598,7 +605,7 @@ const BaseSignInContent: FC<BaseSignInProps> = ({
598605 return ;
599606 }
600607
601- setIsLoading ( true ) ;
608+ setIsSignInInitializationRequestLoading ( true ) ;
602609 setError ( null ) ;
603610 setMessages ( [ ] ) ;
604611
@@ -666,7 +673,7 @@ const BaseSignInContent: FC<BaseSignInProps> = ({
666673 setError ( errorMessage ) ;
667674 onError ?.( err as Error ) ;
668675 } finally {
669- setIsLoading ( false ) ;
676+ setIsSignInInitializationRequestLoading ( false ) ;
670677 }
671678 } ;
672679
@@ -686,7 +693,7 @@ const BaseSignInContent: FC<BaseSignInProps> = ({
686693 touchAllFields ( ) ;
687694 }
688695
689- setIsLoading ( true ) ;
696+ setIsSignInInitializationRequestLoading ( true ) ;
690697 setError ( null ) ;
691698 setMessages ( [ ] ) ;
692699
@@ -955,7 +962,7 @@ const BaseSignInContent: FC<BaseSignInProps> = ({
955962 setError ( errorMessage ) ;
956963 onError ?.( err as Error ) ;
957964 } finally {
958- setIsLoading ( false ) ;
965+ setIsSignInInitializationRequestLoading ( false ) ;
959966 }
960967 } ;
961968
@@ -1020,69 +1027,71 @@ const BaseSignInContent: FC<BaseSignInProps> = ({
10201027
10211028 const errorClasses = clsx ( [ withVendorCSSClassPrefix ( 'signin__error' ) ] , errorClassName ) ;
10221029
1023- const messageClasses = clsx ( [ withVendorCSSClassPrefix ( 'signin__messages' ) ] , messageClassName ) ;
1030+ const messageClasses = clsx ( [ withVendorCSSClassPrefix ( 'signin__messages' ) ] , messageClassName ) ; // Initialize the flow on component mount
10241031
1025- // Initialize the flow on component mount
10261032 useEffect ( ( ) => {
1027- if ( ! isInitialized && ! initializationAttemptedRef . current ) {
1028- initializationAttemptedRef . current = true ;
1033+ if ( isLoading ) {
1034+ return ;
1035+ }
10291036
1030- // Inline initialization to avoid dependency issues
1031- const performInitialization = async ( ) => {
1032- setIsLoading ( true ) ;
1033- setError ( null ) ;
1037+ // React 18.x Strict.Mode has a new check for `Ensuring reusable state` to facilitate an upcoming react feature.
1038+ // https://reactjs.org/docs/strict-mode.html#ensuring-reusable-state
1039+ // This will remount all the useEffects to ensure that there are no unexpected side effects.
1040+ // When react remounts the SignIn, it will send two authorize requests.
1041+ // https://github.com/reactwg/react-18/discussions/18#discussioncomment-795623
1042+ if ( reRenderCheckRef . current ) {
1043+ return ;
1044+ }
10341045
1035- try {
1036- const response = await onInitialize ( ) ;
1046+ reRenderCheckRef . current = true ;
10371047
1038- setCurrentFlow ( response ) ;
1039- setIsInitialized ( true ) ;
1040- onFlowChange ?. ( response ) ;
1048+ ( async ( ) => {
1049+ setIsSignInInitializationRequestLoading ( true ) ;
1050+ setError ( null ) ;
10411051
1042- if ( response ?. flowStatus === EmbeddedSignInFlowStatus . SuccessCompleted ) {
1043- onSuccess ?.( ( response as any ) . authData || { } ) ;
1044- return ;
1045- }
1052+ try {
1053+ const response = await onInitialize ( ) ;
10461054
1047- if ( response ?. nextStep ?. authenticators ?. length > 0 ) {
1048- if (
1049- response . nextStep . stepType === EmbeddedSignInFlowStepType . MultiOptionsPrompt &&
1050- response . nextStep . authenticators . length > 1
1051- ) {
1052- setCurrentAuthenticator ( null ) ;
1053- } else {
1054- const authenticator = response . nextStep . authenticators [ 0 ] ;
1055- setCurrentAuthenticator ( authenticator ) ;
1056- setupFormFields ( authenticator ) ;
1057- }
1058- }
1055+ setCurrentFlow ( response ) ;
1056+ setIsInitialized ( true ) ;
1057+ onFlowChange ?.( response ) ;
10591058
1060- if ( response && 'nextStep' in response && response . nextStep && 'messages' in response . nextStep ) {
1061- const stepMessages = ( response . nextStep as any ) . messages || [ ] ;
1062- setMessages (
1063- stepMessages . map ( ( msg : any ) => ( {
1064- type : msg . type || 'INFO' ,
1065- message : msg . message || '' ,
1066- } ) ) ,
1067- ) ;
1068- }
1069- } catch ( err ) {
1070- const errorMessage = err instanceof AsgardeoAPIError ? err . message : t ( 'errors.sign.in.initialization' ) ;
1071- setError ( errorMessage ) ;
1072- onError ?.( err as Error ) ;
1073- } finally {
1074- setIsLoading ( false ) ;
1059+ if ( response ?. flowStatus === EmbeddedSignInFlowStatus . SuccessCompleted ) {
1060+ onSuccess ?.( ( response as any ) . authData || { } ) ;
1061+ return ;
10751062 }
1076- } ;
10771063
1078- performInitialization ( ) ;
1079- }
1064+ if ( response ?. nextStep ?. authenticators ?. length > 0 ) {
1065+ if (
1066+ response . nextStep . stepType === EmbeddedSignInFlowStepType . MultiOptionsPrompt &&
1067+ response . nextStep . authenticators . length > 1
1068+ ) {
1069+ setCurrentAuthenticator ( null ) ;
1070+ } else {
1071+ const authenticator = response . nextStep . authenticators [ 0 ] ;
1072+ setCurrentAuthenticator ( authenticator ) ;
1073+ setupFormFields ( authenticator ) ;
1074+ }
1075+ }
10801076
1081- // Cleanup function to reset initialization state on unmount
1082- return ( ) => {
1083- initializationAttemptedRef . current = false ;
1084- } ;
1085- } , [ isInitialized ] ) ;
1077+ if ( response && 'nextStep' in response && response . nextStep && 'messages' in response . nextStep ) {
1078+ const stepMessages = ( response . nextStep as any ) . messages || [ ] ;
1079+ setMessages (
1080+ stepMessages . map ( ( msg : any ) => ( {
1081+ type : msg . type || 'INFO' ,
1082+ message : msg . message || '' ,
1083+ } ) ) ,
1084+ ) ;
1085+ }
1086+ } catch ( err ) {
1087+ const errorMessage = err instanceof AsgardeoAPIError ? err . message : t ( 'errors.sign.in.initialization' ) ;
1088+ setError ( errorMessage ) ;
1089+ onError ?.( err as Error ) ;
1090+ } finally {
1091+ setIsSignInInitializationRequestLoading ( false ) ;
1092+ }
1093+ } ) ( ) ;
1094+ } , [ isLoading ] ) ;
10861095
10871096 if ( ! isInitialized && isLoading ) {
10881097 return (
0 commit comments