@@ -15,6 +15,7 @@ import { handleValueOrFn, noop } from '@clerk/shared/utils';
15
15
import type {
16
16
__experimental_CommerceNamespace ,
17
17
__experimental_PricingTableProps ,
18
+ __internal_ComponentNavigationContext ,
18
19
__internal_UserVerificationModalProps ,
19
20
AuthenticateWithCoinbaseWalletParams ,
20
21
AuthenticateWithGoogleOneTapParams ,
@@ -40,10 +41,12 @@ import type {
40
41
JoinWaitlistParams ,
41
42
ListenerCallback ,
42
43
NavigateOptions ,
44
+ NextTaskParams ,
43
45
OrganizationListProps ,
44
46
OrganizationProfileProps ,
45
47
OrganizationResource ,
46
48
OrganizationSwitcherProps ,
49
+ PendingSessionResource ,
47
50
PublicKeyCredentialCreationOptionsWithoutExtensions ,
48
51
PublicKeyCredentialRequestOptionsWithoutExtensions ,
49
52
PublicKeyCredentialWithAuthenticatorAssertionResponse ,
@@ -201,15 +204,7 @@ export class Clerk implements ClerkInterface {
201
204
#options: ClerkOptions = { } ;
202
205
#pageLifecycle: ReturnType < typeof createPageLifecycle > | null = null ;
203
206
#touchThrottledUntil = 0 ;
204
- #componentNavigationContext: {
205
- navigate : (
206
- to : string ,
207
- options ?: {
208
- searchParams ?: URLSearchParams ;
209
- } ,
210
- ) => Promise < unknown > ;
211
- basePath : string ;
212
- } | null = null ;
207
+ #componentNavigationContext: __internal_ComponentNavigationContext | null = null ;
213
208
214
209
public __internal_getCachedResources :
215
210
| ( ( ) => Promise < { client : ClientJSONSnapshot | null ; environment : EnvironmentJSONSnapshot | null } > )
@@ -975,11 +970,6 @@ export class Clerk implements ClerkInterface {
975
970
session = ( this . client . sessions . find ( x => x . id === session ) as SignedInSessionResource ) || null ;
976
971
}
977
972
978
- if ( session ?. status === 'pending' ) {
979
- await this . #handlePendingSession( session ) ;
980
- return ;
981
- }
982
-
983
973
let newSession = session === undefined ? this . session : session ;
984
974
985
975
// At this point, the `session` variable should contain either an `SignedInSessionResource`
@@ -1002,6 +992,11 @@ export class Clerk implements ClerkInterface {
1002
992
}
1003
993
}
1004
994
995
+ if ( newSession ?. status === 'pending' ) {
996
+ await this . #handlePendingSession( newSession ) ;
997
+ return ;
998
+ }
999
+
1005
1000
if ( session ?. lastActiveToken ) {
1006
1001
eventBus . dispatch ( events . TokenUpdate , { token : session . lastActiveToken } ) ;
1007
1002
}
@@ -1069,16 +1064,18 @@ export class Clerk implements ClerkInterface {
1069
1064
await onAfterSetActive ( ) ;
1070
1065
} ;
1071
1066
1072
- #handlePendingSession = async ( session : SignedInSessionResource ) => {
1067
+ #handlePendingSession = async ( session : PendingSessionResource ) => {
1073
1068
if ( ! this . environment ) {
1074
1069
return ;
1075
1070
}
1076
1071
1077
- // Handles multi-session scenario when switching from `active`
1078
- // to `pending`
1072
+ let newSession : SignedInSessionResource | null = session ;
1073
+
1074
+ // Handles multi-session scenario when switching between `pending` sessions
1075
+ // and satisfying task requirements such as organization selection
1079
1076
if ( inActiveBrowserTab ( ) || ! this . #options. standardBrowser ) {
1080
1077
await this . #touchCurrentSession( session ) ;
1081
- session = this . #getSessionFromClient( session . id ) ?? session ;
1078
+ newSession = this . #getSessionFromClient( session . id ) ?? session ;
1082
1079
}
1083
1080
1084
1081
// Syncs __session and __client_uat, in case the `pending` session
@@ -1088,13 +1085,50 @@ export class Clerk implements ClerkInterface {
1088
1085
eventBus . dispatch ( events . TokenUpdate , { token : null } ) ;
1089
1086
}
1090
1087
1091
- if ( session . currentTask ) {
1088
+ if ( newSession ? .currentTask ) {
1092
1089
await navigateToTask ( session . currentTask , {
1093
1090
globalNavigate : this . navigate ,
1094
1091
componentNavigationContext : this . #componentNavigationContext,
1095
1092
options : this . #options,
1096
1093
environment : this . environment ,
1097
1094
} ) ;
1095
+
1096
+ // Delay updating session accessors until active status transition to prevent premature component unmounting.
1097
+ // This is particularly important when SignIn components are wrapped in SignedOut components,
1098
+ // as early state updates could cause unwanted unmounting during the transition.
1099
+ this . #setAccessors( session ) ;
1100
+ }
1101
+
1102
+ this . #emit( ) ;
1103
+ } ;
1104
+
1105
+ public __experimental_nextTask = async ( { redirectUrlComplete } : NextTaskParams = { } ) : Promise < void > => {
1106
+ const session = await this . session ?. reload ( ) ;
1107
+ if ( ! session || ! this . environment ) {
1108
+ return ;
1109
+ }
1110
+
1111
+ if ( session . status === 'pending' ) {
1112
+ await navigateToTask ( session . currentTask , {
1113
+ options : this . #options,
1114
+ environment : this . environment ,
1115
+ globalNavigate : this . navigate ,
1116
+ componentNavigationContext : this . #componentNavigationContext,
1117
+ } ) ;
1118
+ return ;
1119
+ }
1120
+
1121
+ const tracker = createBeforeUnloadTracker ( this . #options. standardBrowser ) ;
1122
+ const defaultRedirectUrlComplete = this . client ?. signUp ? this . buildAfterSignUpUrl ( ) : this . buildAfterSignUpUrl ( ) ;
1123
+
1124
+ this . #setTransitiveState( ) ;
1125
+
1126
+ await tracker . track ( async ( ) => {
1127
+ await this . navigate ( redirectUrlComplete ?? defaultRedirectUrlComplete ) ;
1128
+ } ) ;
1129
+
1130
+ if ( tracker . isUnloading ( ) ) {
1131
+ return ;
1098
1132
}
1099
1133
1100
1134
this . #setAccessors( session ) ;
@@ -1128,15 +1162,7 @@ export class Clerk implements ClerkInterface {
1128
1162
return unsubscribe ;
1129
1163
} ;
1130
1164
1131
- public __internal_setComponentNavigationContext = ( context : {
1132
- navigate : (
1133
- to : string ,
1134
- options ?: {
1135
- searchParams ?: URLSearchParams ;
1136
- } ,
1137
- ) => Promise < unknown > ;
1138
- basePath : string ;
1139
- } ) => {
1165
+ public __internal_setComponentNavigationContext = ( context : __internal_ComponentNavigationContext ) => {
1140
1166
this . #componentNavigationContext = context ;
1141
1167
1142
1168
return ( ) => ( this . #componentNavigationContext = null ) ;
@@ -2269,6 +2295,11 @@ export class Clerk implements ClerkInterface {
2269
2295
}
2270
2296
} ;
2271
2297
2298
+ /**
2299
+ * Temporarily clears the accessors before emitting changes to React context state.
2300
+ * This is used during transitions like sign-out or session changes to prevent UI flickers
2301
+ * such as unexpected unmount of control components
2302
+ */
2272
2303
#setTransitiveState = ( ) => {
2273
2304
this . session = undefined ;
2274
2305
this . organization = undefined ;
0 commit comments