Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/odd-coins-carry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@clerk/elements': patch
---

This release includes various smaller fixes and one dependency update:

- `xstate` was updated from `5.12.0` to `5.13.0`
- Previously, the contents of the `fallback` prop were sometimes shown even if the user wasn't on the `start` step. This bug is fixed now.
- Upon completion of an sign-in/sign-up attempt, don't immediately return to the `start` step. This fixes the issue of a "flash of content" that could e.g. be seen during sign-in with OAuth providers.
- Some underlying fixes in Clerk Elements' XState logic were applied to make sure that during a sign-in/sign-up attempt the state is properly maintained. For example, if you visit an already completed attempt (some step of that flow) it now properly keeps track of that state.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/elements/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
"@statelyai/inspect": "^0.3.0",
"@xstate/react": "^4.1.1",
"client-only": "^0.0.1",
"xstate": "^5.12.0"
"xstate": "^5.13.0"
},
"devDependencies": {
"@clerk/clerk-react": "5.0.4",
Expand Down
22 changes: 13 additions & 9 deletions packages/elements/src/internals/machines/sign-in/router.machine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { ClerkElementsError, ClerkElementsRuntimeError } from '~/internals/error
import { ThirdPartyMachine, ThirdPartyMachineId } from '~/internals/machines/third-party';
import { shouldUseVirtualRouting } from '~/internals/machines/utils/next';

import { FormMachine } from '../form';
import { SignInResetPasswordMachine } from './reset-password.machine';
import type {
SignInRouterContext,
Expand Down Expand Up @@ -41,6 +42,7 @@ export const SignInRouterMachineId = 'SignInRouter';
export const SignInRouterMachine = setup({
actors: {
firstFactorMachine: SignInFirstFactorMachine,
formMachine: FormMachine,
resetPasswordMachine: SignInResetPasswordMachine,
startMachine: SignInStartMachine,
secondFactorMachine: SignInSecondFactorMachine,
Expand All @@ -60,8 +62,8 @@ export const SignInRouterMachine = setup({
},
navigateExternal: ({ context }, { path }: { path: string }) => context.router?.push(path),
raiseNext: raise({ type: 'NEXT' }),
setActive({ context, event }) {
if (context.exampleMode) return;
setActive: enqueueActions(({ enqueue, check, context, event }) => {
if (check('isExampleMode')) return;

const lastActiveSessionId = context.clerk.client.lastActiveSessionId;
const createdSessionId = ((event as SignInRouterNextEvent)?.resource || context.clerk.client.signIn)
Expand All @@ -71,7 +73,9 @@ export const SignInRouterMachine = setup({

const beforeEmit = () => context.router?.push(context.clerk.buildAfterSignInUrl());
void context.clerk.setActive({ session, beforeEmit });
},

enqueue.raise({ type: 'RESET' }, { delay: 2000 }); // Reset machine after 2s delay.
}),
setError: assign({
error: (_, { error }: { error?: ClerkElementsError }) => {
if (error) return error;
Expand Down Expand Up @@ -182,6 +186,7 @@ export const SignInRouterMachine = setup({
},
})),
},
RESET: '.Idle',
},
states: {
Idle: {
Expand Down Expand Up @@ -221,6 +226,10 @@ export const SignInRouterMachine = setup({
guard: 'needsCallback',
target: 'Callback',
},
{
guard: 'isComplete',
actions: 'setActive',
},
{
guard: 'isLoggedInAndSingleSession',
actions: [
Expand Down Expand Up @@ -280,7 +289,6 @@ export const SignInRouterMachine = setup({
{
guard: 'isComplete',
actions: 'setActive',
target: 'Start',
},
{
guard: 'statusNeedsFirstFactor',
Expand Down Expand Up @@ -318,7 +326,6 @@ export const SignInRouterMachine = setup({
{
guard: 'isComplete',
actions: 'setActive',
target: 'Start',
},
{
guard: 'statusNeedsSecondFactor',
Expand Down Expand Up @@ -385,7 +392,6 @@ export const SignInRouterMachine = setup({
{
guard: 'isComplete',
actions: 'setActive',
target: 'Start',
},
{
guard: 'statusNeedsNewPassword',
Expand Down Expand Up @@ -413,7 +419,6 @@ export const SignInRouterMachine = setup({
{
guard: 'isComplete',
actions: 'setActive',
target: 'Start',
},
{
guard: 'statusNeedsFirstFactor',
Expand All @@ -439,9 +444,8 @@ export const SignInRouterMachine = setup({
target: 'Start',
},
{
guard: or(['isComplete', 'hasAuthenticatedViaClerkJS']),
guard: or(['isLoggedIn', 'isComplete', 'hasAuthenticatedViaClerkJS']),
actions: 'setActive',
target: 'Start',
},
{
guard: 'statusNeedsIdentifier',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type {
BaseRouterNextEvent,
BaseRouterPrevEvent,
BaseRouterRedirectEvent,
BaseRouterResetEvent,
BaseRouterSetClerkEvent,
BaseRouterStartEvent,
BaseRouterTransferEvent,
Expand Down Expand Up @@ -54,6 +55,7 @@ export type SignInRouterForgotPasswordEvent = { type: 'NAVIGATE.FORGOT_PASSWORD'
export type SignInRouterErrorEvent = BaseRouterErrorEvent;
export type SignInRouterTransferEvent = BaseRouterTransferEvent;
export type SignInRouterRedirectEvent = BaseRouterRedirectEvent;
export type SignInRouterResetEvent = BaseRouterResetEvent;
export type SignInRouterLoadingEvent = BaseRouterLoadingEvent<'start' | 'verifications' | 'reset-password'>;
export type SignInRouterSetClerkEvent = BaseRouterSetClerkEvent;
export type SignInRouterSubmitEvent = { type: 'SUBMIT' };
Expand All @@ -77,6 +79,7 @@ export type SignInRouterEvents =
| SignInRouterErrorEvent
| SignInRouterTransferEvent
| SignInRouterRedirectEvent
| SignInRouterResetEvent
| SignInVerificationFactorUpdateEvent
| SignInRouterLoadingEvent
| SignInRouterSetClerkEvent
Expand Down
24 changes: 15 additions & 9 deletions packages/elements/src/internals/machines/sign-up/router.machine.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { joinURL } from '@clerk/shared/url';
import type { SignUpStatus, VerificationStatus } from '@clerk/types';
import type { NonReducibleUnknown } from 'xstate';
import { and, assign, log, not, or, raise, sendTo, setup, spawnChild } from 'xstate';
import { and, assign, enqueueActions, log, not, or, raise, sendTo, setup, spawnChild } from 'xstate';

import {
ERROR_CODES,
Expand Down Expand Up @@ -59,15 +59,20 @@ export const SignUpRouterMachine = setup({
},
navigateExternal: ({ context }, { path }: { path: string }) => context.router?.push(path),
raiseNext: raise({ type: 'NEXT' }),
setActive({ context, event }, params?: { sessionId?: string; useLastActiveSession?: boolean }) {
const session =
params?.sessionId ||
(params?.useLastActiveSession && context.clerk.client.lastActiveSessionId) ||
((event as SignUpRouterNextEvent)?.resource || context.clerk.client.signUp).createdSessionId;
setActive: (_, params?: { sessionId?: string; useLastActiveSession?: boolean }) =>
enqueueActions(({ enqueue, check, context, event }) => {
if (check('isExampleMode')) return;

const beforeEmit = () => context.router?.push(context.clerk.buildAfterSignUpUrl());
void context.clerk.setActive({ session, beforeEmit });
},
const session =
params?.sessionId ||
(params?.useLastActiveSession && context.clerk.client.lastActiveSessionId) ||
((event as SignUpRouterNextEvent)?.resource || context.clerk.client.signUp).createdSessionId;

const beforeEmit = () => context.router?.push(context.clerk.buildAfterSignUpUrl());
void context.clerk.setActive({ session, beforeEmit });

enqueue.raise({ type: 'RESET' }, { delay: 2000 }); // Reset machine after 2s delay.
}),
setError: assign({
error: (_, { error }: { error?: ClerkElementsError }) => {
if (error) return error;
Expand Down Expand Up @@ -182,6 +187,7 @@ export const SignUpRouterMachine = setup({
},
})),
},
RESET: '.Idle',
},
states: {
Idle: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type {
BaseRouterNextEvent,
BaseRouterPrevEvent,
BaseRouterRedirectEvent,
BaseRouterResetEvent,
BaseRouterSetClerkEvent,
BaseRouterStartEvent,
BaseRouterTransferEvent,
Expand Down Expand Up @@ -46,6 +47,7 @@ export type SignUpRouterPrevEvent = BaseRouterPrevEvent;
export type SignUpRouterErrorEvent = BaseRouterErrorEvent;
export type SignUpRouterTransferEvent = BaseRouterTransferEvent;
export type SignUpRouterRedirectEvent = BaseRouterRedirectEvent;
export type SignUpRouterResetEvent = BaseRouterResetEvent;
export type SignUpRouterLoadingEvent = BaseRouterLoadingEvent<'start' | 'verifications' | 'continue'>;
export type SignUpRouterSetClerkEvent = BaseRouterSetClerkEvent;

Expand All @@ -64,6 +66,7 @@ export type SignUpRouterEvents =
| SignUpRouterErrorEvent
| SignUpRouterTransferEvent
| SignUpRouterRedirectEvent
| SignUpRouterResetEvent
| SignUpRouterLoadingEvent
| SignUpRouterSetClerkEvent;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export type BaseRouterLoadingStep = 'start' | 'verifications' | 'continue' | 're
export type BaseRouterNextEvent<T extends ClerkResource> = { type: 'NEXT'; resource?: T };
export type BaseRouterPrevEvent = { type: 'NAVIGATE.PREVIOUS' };
export type BaseRouterStartEvent = { type: 'NAVIGATE.START' };
export type BaseRouterResetEvent = { type: 'RESET' };
export type BaseRouterErrorEvent = { type: 'ERROR'; error: Error };
export type BaseRouterTransferEvent = { type: 'TRANSFER' };
export type BaseRouterLoadingEvent<TSteps extends BaseRouterLoadingStep> = (
Expand Down
13 changes: 9 additions & 4 deletions packages/elements/src/react/sign-in/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ function SignInFlowProvider({ children, exampleMode }: SignInFlowProviderProps)
actor.send(evt);
}
});
}, [clerk, exampleMode, formRef, router]);

// eslint-disable-next-line react-hooks/exhaustive-deps
}, [clerk, exampleMode, formRef?.id, !!router]);

@tmilewski tmilewski May 13, 2024

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw that formRef and router were causing many unnecessary re-renders. I narrowed the checks down here and in sign up to optimize.


return <SignInRouterCtx.Provider actorRef={actor}>{children}</SignInRouterCtx.Provider>;
}
Expand Down Expand Up @@ -94,6 +96,7 @@ export function SignInRoot({

// TODO: eventually we'll rely on the framework SDK to specify its host router, but for now we'll default to Next.js
const router = useNextRouter();
const isRootPath = path === router.pathname();

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is required, on both Sign Up and Sign In, otherwise, in the case of callbacks, the fallback will show regardless.


return (
<Router
Expand All @@ -102,9 +105,11 @@ export function SignInRoot({
>
<FormStoreProvider>
<SignInFlowProvider exampleMode={exampleMode}>
<ClerkLoading>
<Form>{fallback}</Form>
</ClerkLoading>
{isRootPath ? (
<ClerkLoading>
<Form>{fallback}</Form>
</ClerkLoading>
) : null}
<ClerkLoaded>{children}</ClerkLoaded>
</SignInFlowProvider>
</FormStoreProvider>
Expand Down
2 changes: 1 addition & 1 deletion packages/elements/src/react/sign-in/verifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export function SignInStrategy({ children, name }: SignInStrategyProps) {
}

return () => {
if (factorCtx) {
if (factorCtx?.getSnapshot().status === 'active') {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure we only run this event on an active machine.

factorCtx.send({ type: 'STRATEGY.UNREGISTER', factor: name as unknown as SignInFactor });
}
};
Expand Down
12 changes: 8 additions & 4 deletions packages/elements/src/react/sign-up/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ function SignUpFlowProvider({ children, exampleMode }: SignUpFlowProviderProps)
ref.send(evt);
}
});
}, [clerk, exampleMode, formRef, router]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [clerk, exampleMode, formRef?.id, !!router]);

return isReady ? <SignUpRouterCtx.Provider actorRef={ref}>{children}</SignUpRouterCtx.Provider> : null;
}
Expand Down Expand Up @@ -91,6 +92,7 @@ export function SignUpRoot({

// TODO: eventually we'll rely on the framework SDK to specify its host router, but for now we'll default to Next.js
const router = useNextRouter();
const isRootPath = path === router.pathname();

return (
<Router
Expand All @@ -99,9 +101,11 @@ export function SignUpRoot({
>
<FormStoreProvider>
<SignUpFlowProvider exampleMode={exampleMode}>
<ClerkLoading>
<Form>{fallback}</Form>
</ClerkLoading>
{isRootPath ? (
<ClerkLoading>
<Form>{fallback}</Form>
</ClerkLoading>
) : null}
<ClerkLoaded>{children}</ClerkLoaded>
</SignUpFlowProvider>
</FormStoreProvider>
Expand Down