fix: revalidate draft room when Clerk JWT expires at load time

Clerk's short-lived JWTs (~1 min) can expire between navigations.
Server-side clerkMiddleware can't refresh them for client-side loader
fetches, so getAuth() returns userId=null — causing userTeam=undefined,
the queue button to disappear, and the tab to default to "board".

Added a useEffect that detects the mismatch: if the Clerk client SDK
has a valid userId but the loader ran without one, trigger revalidate()
so the loader re-runs with a fresh token and returns correct
userTeam/userQueue data.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Chris Parsons 2026-02-28 09:48:42 -08:00
parent f6fe3815cb
commit d21ff5d977

View file

@ -1,4 +1,5 @@
import { useLoaderData, Link, useRevalidator } from "react-router"; import { useLoaderData, Link, useRevalidator } from "react-router";
import { useAuth } from "@clerk/react-router";
import { eq, and, asc, desc, inArray } from "drizzle-orm"; import { eq, and, asc, desc, inArray } from "drizzle-orm";
import { database } from "~/database/context"; import { database } from "~/database/context";
import * as schema from "~/database/schema"; import * as schema from "~/database/schema";
@ -229,6 +230,7 @@ export default function DraftRoom() {
ownerMap, ownerMap,
} = useLoaderData<typeof loader>(); } = useLoaderData<typeof loader>();
const { revalidate, state: revalidatorState } = useRevalidator(); const { revalidate, state: revalidatorState } = useRevalidator();
const { userId: clerkUserId } = useAuth();
const { isConnected, connectionError, isReconnecting, reconnectCount, on, off } = useDraftSocket(season.id, userTeam?.id); const { isConnected, connectionError, isReconnecting, reconnectCount, on, off } = useDraftSocket(season.id, userTeam?.id);
const { const {
permissionState: notificationsPermission, permissionState: notificationsPermission,
@ -268,6 +270,15 @@ export default function DraftRoom() {
} }
}, [reconnectCount, revalidate]); }, [reconnectCount, revalidate]);
// Guard against Clerk JWT expiry race: if the loader ran without a userId (expired
// short-lived JWT) but the Clerk client SDK has a valid session, revalidate so the
// loader re-runs with a fresh token and returns correct userTeam/userQueue data.
useEffect(() => {
if (clerkUserId && !currentUserId) {
revalidate();
}
}, [clerkUserId, currentUserId, revalidate]);
// Track revalidation lifecycle to sync local state from fresh loader data. // Track revalidation lifecycle to sync local state from fresh loader data.
// //
// The problem: `picks` and `currentPick` are local state initialized once // The problem: `picks` and `currentPick` are local state initialized once