Conversation
WalkthroughModified Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (4)
src/state/KindeProvider.tsx (4)
228-234: URL parsing runs on every render.
new URLSearchParams(window.location.search)executes on every render, even though the refs only initialize once. Consider memoizing this or using a lazy initialization pattern:- // Check for invitation_code synchronously before any hooks/rendering - const params = new URLSearchParams(window.location.search); - const hasInvitationCode = params.has("invitation_code"); - const invitationCodeRef = useRef<string | null>( - hasInvitationCode ? params.get("invitation_code") : null, - ); - const isRedirectingRef = useRef(hasInvitationCode); + // Check for invitation_code synchronously before any hooks/rendering + const invitationCodeRef = useRef<string | null>(null); + const isRedirectingRef = useRef(false); + + // Initialize refs only once + if (invitationCodeRef.current === null && isRedirectingRef.current === false) { + const params = new URLSearchParams(window.location.search); + if (params.has("invitation_code")) { + invitationCodeRef.current = params.get("invitation_code"); + isRedirectingRef.current = true; + } + }Alternatively, use a dedicated initialization ref to track whether parsing has been done.
375-376:loginRefis assigned but never read.
loginRefis assigned at line 436 but never used elsewhere. The useEffect at line 439 directly uses thelogincallback from the dependency array instead ofloginRef.current. Either remove the unused ref or use it for the intended immediate access pattern.If the ref is no longer needed:
const initRef = useRef(false); - const loginRef = useRef<typeof login | null>(null); const redirectInitiatedRef = useRef(false);And remove the assignment at lines 435-436.
439-456: Consider invokingonErrorcallback on invitation redirect failure.The catch block logs the error but doesn't notify the application via the
onErrorcallback. This could leave users on a blank screen (since children aren't rendered during redirect) without understanding what went wrong.}).catch((error) => { console.error("Error processing invitation code:", error); + mergedCallbacks.onError?.( + { + error: "ERR_INVITATION_REDIRECT", + errorDescription: String(error), + }, + {}, + {} as KindeContextProps, + ); isRedirectingRef.current = false; redirectInitiatedRef.current = false; });Also note: the
login &&check on line 443 is always true sinceloginis a stableuseCallbackreference.
788-793: Unused variable declaration before early return.The
paramsvariable declared at line 788 is unused whenisRedirectingRef.currentis true (the early return at line 792). Additionally, it shadows theparamsvariable from line 229. Consider removing this redundant parsing or moving it after the guard clause:const init = useCallback(async () => { if (initRef.current) return; try { - const params = new URLSearchParams(window.location.search); - // Skip initialization if redirecting for invitation (handled in useEffect above) if (isRedirectingRef.current) { return; } + const params = new URLSearchParams(window.location.search); + try { initRef.current = true;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package.jsonis excluded by!**/*.json
📒 Files selected for processing (1)
src/state/KindeProvider.tsx(6 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-05T12:17:11.365Z
Learnt from: DanielRivers
Repo: kinde-oss/kinde-auth-react PR: 173
File: src/state/KindeProvider.tsx:584-585
Timestamp: 2025-09-05T12:17:11.365Z
Learning: In Kinde auth React applications, the callback URL (redirectUri) is always absolute because it's required for hosted auth to work properly.
Applied to files:
src/state/KindeProvider.tsx
🔇 Additional comments (1)
src/state/KindeProvider.tsx (1)
890-893: LGTM!The conditional render suppression correctly prevents rendering children during the invitation redirect flow while respecting the
forceChildrenRenderescape hatch.
dtoxvanilla1991
left a comment
There was a problem hiding this comment.
great stuff. Left my thoughts.
|
|
||
| // Skip initialization if redirecting for invitation (handled in useEffect above) | ||
| if (isRedirectingRef.current) { | ||
| return; |
There was a problem hiding this comment.
This early-return skips init() entirely while redirecting (comment explains why of course). Combined with ref-based flagging though, a failed invitation redirect won’t automatically re-run init later, leaving the SDK permanently uninitialized. Thought: redirecting flag in state + ensure init runs once redirecting becomes false. Or is failed invitation link never an option?
| }; | ||
| }, [init]); | ||
|
|
||
| // Don't render children if redirecting for invitation |
There was a problem hiding this comment.
Early return of an empty fragment while redirecting is OK only if there’s a guaranteed rerender/unblock path. I believe, as implemented, the unblock path mutates a ref (no rerender), so this can dead-end into a blank screen. Should we do state-driven gating?
| "private": false, | ||
| "dependencies": { | ||
| "@kinde/js-utils": "0.29.0" | ||
| "@kinde/js-utils": "0.29.1-5" |
There was a problem hiding this comment.
Do we want to get pnpm-lock.yaml updated accordingly in the PR, and ok to pin a prerelease in a public SDK release line?
| }: KindeProviderProps) => { | ||
| const mergedCallbacks = { ...defaultCallbacks, ...callbacks }; | ||
| // Check for invitation_code synchronously before any hooks/rendering | ||
| const params = new URLSearchParams(window.location.search); |
There was a problem hiding this comment.
This reads window.location.search during render. Now, we know and aim for this to always be used on the client-side since we need to have access to window and all other browsers' APIs. That said, I think there are several things to consider:
- Tests/build tooling can evaluate components outside a real browser (Vitest, Storybook, prerenderers). Guarding
windowduring render would avoid brittle failures. - Doing
new URLSearchParams(window.location.search)during render I believe also violates the “no side-effectful / environment-dependent reads during render” guideline for React libraries; it’s easy to make this deterministic via a lazy initializer + effect.
Thus maybe prefer typeof window !== 'undefined' guard + lazy useState initializer and do the redirect work inside useEffect. Lmk your thoughts 💭
| }).catch((error) => { | ||
| console.error("Error processing invitation code:", error); | ||
| isRedirectingRef.current = false; | ||
| redirectInitiatedRef.current = false; |
There was a problem hiding this comment.
isRedirectingRef is used to gate redirect and later set to false on error, but refs don’t trigger re-render. If login() fails, consumers can get stuck rendering nothing (seeing later early-return). So once again, should we use state for redirecting so failure unblocks UI and allows init to continue. Or login() doesnt fail in this way?
| ); | ||
|
|
||
| // Store login in ref for immediate access | ||
| loginRef.current = login; |
There was a problem hiding this comment.
loginRef isn't being used except to store the value of login.
Explain your changes
Add support for invitations
Checklist
🛟 If you need help, consider asking for advice over in the Kinde community.