Skip to content

Commit

Permalink
JS: Fix clearing auth and avoid useLayoutEffect warning (#29027)
Browse files Browse the repository at this point in the history
GitOrigin-RevId: fbbbc95e4a8c9414cf90ed93085ef39002985b8f
  • Loading branch information
xixixao authored and Convex, Inc. committed Aug 21, 2024
1 parent d46fc17 commit 7a746ee
Showing 1 changed file with 85 additions and 30 deletions.
115 changes: 85 additions & 30 deletions npm-packages/convex/src/react/ConvexAuthState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, {
createContext,
ReactNode,
useContext,
useLayoutEffect,
useEffect,
useState,
} from "react";
import { AuthTokenFetcher } from "../browser/sync/client.js";
Expand Down Expand Up @@ -89,7 +89,63 @@ export function ConvexProviderWithAuth({
boolean | null
>(null);

useLayoutEffect(() => {
// If the useAuth went back to the loading state (which is unusual but possible)
// reset the Convex auth state to null so that we can correctly
// transition the state from "loading" to "authenticated"
// without going through "unauthenticated".
if (isLoading && isConvexAuthenticated !== null) {
setIsConvexAuthenticated(null);
}

if (!isLoading && !isAuthenticated && isConvexAuthenticated !== false) {
setIsConvexAuthenticated(false);
}

return (
<ConvexAuthContext.Provider
value={{
isLoading: isConvexAuthenticated === null,
isAuthenticated: isAuthenticated && (isConvexAuthenticated ?? false),
}}
>
<ConvexAuthStateFirstEffect
isAuthenticated={isAuthenticated}
fetchAccessToken={fetchAccessToken}
isLoading={isLoading}
client={client}
setIsConvexAuthenticated={setIsConvexAuthenticated}
/>
<ConvexProvider client={client as any}>{children}</ConvexProvider>
<ConvexAuthStateLastEffect
isAuthenticated={isAuthenticated}
fetchAccessToken={fetchAccessToken}
isLoading={isLoading}
client={client}
/>
</ConvexAuthContext.Provider>
);
}

// First child ensures we `setAuth` before
// other child components subscribe to queries via `useEffect`.
function ConvexAuthStateFirstEffect({
isAuthenticated,
fetchAccessToken,
isLoading,
client,
setIsConvexAuthenticated,
}: {
isAuthenticated: boolean;
fetchAccessToken: (args: {
forceRefreshToken: boolean;
}) => Promise<string | null>;
isLoading: boolean;
client: IConvexReactClient;
setIsConvexAuthenticated: React.Dispatch<
React.SetStateAction<boolean | null>
>;
}) {
useEffect(() => {
let isThisEffectRelevant = true;
if (isAuthenticated) {
client.setAuth(fetchAccessToken, (isAuthenticated) => {
Expand All @@ -107,39 +163,38 @@ export function ConvexProviderWithAuth({
);
};
}
}, [isAuthenticated, fetchAccessToken, isLoading, client]);
}, [
isAuthenticated,
fetchAccessToken,
isLoading,
client,
setIsConvexAuthenticated,
]);
return null;
}

// Clear auth later, so that queries from
// unmounted child components unsubscribe first
// and rerun without auth on the server
useLayoutEffect(() => {
// Last child ensures we `clearAuth` last,
// so that queries from unmounted sibling components
// unsubscribe first and don't rerun without auth on the server
function ConvexAuthStateLastEffect({
isAuthenticated,
fetchAccessToken,
isLoading,
client,
}: {
isAuthenticated: boolean;
fetchAccessToken: (args: {
forceRefreshToken: boolean;
}) => Promise<string | null>;
isLoading: boolean;
client: IConvexReactClient;
}) {
useEffect(() => {
if (isAuthenticated) {
return () => {
client.clearAuth();
};
}
}, [isAuthenticated, fetchAccessToken, isLoading, client]);

// If the useAuth went back to the loading state (which is unusual but possible)
// reset the Convex auth state to null so that we can correctly
// transition the state from "loading" to "authenticated"
// without going through "unauthenticated".
if (isLoading && isConvexAuthenticated !== null) {
setIsConvexAuthenticated(null);
}

if (!isLoading && !isAuthenticated && isConvexAuthenticated !== false) {
setIsConvexAuthenticated(false);
}

return (
<ConvexAuthContext.Provider
value={{
isLoading: isConvexAuthenticated === null,
isAuthenticated: isAuthenticated && (isConvexAuthenticated ?? false),
}}
>
<ConvexProvider client={client as any}>{children}</ConvexProvider>
</ConvexAuthContext.Provider>
);
return null;
}

0 comments on commit 7a746ee

Please sign in to comment.