Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

useUser() returns undefined unexpectedly on initial render #582

Open
wieringen opened this issue Jun 29, 2023 · 5 comments
Open

useUser() returns undefined unexpectedly on initial render #582

wieringen opened this issue Jun 29, 2023 · 5 comments

Comments

@wieringen
Copy link

wieringen commented Jun 29, 2023

Version info

React:
18.2.0
Next:
13.2.4
Firebase:
9.22.1
ReactFire:
4.2.3

Steps to reproduce

I initialize reactfire using the code below. As you can see, I have a guard checking if the user is signed in. And only if the user is signed in the children/pages are rendered.

function AuthWrapper(props: any) {
  const app = useFirebaseApp();
  const auth = getAuth(app);
  return <AuthProvider sdk={auth}>{props.children}</AuthProvider>;
}

function AuthGuard(props: React.PropsWithChildren<{ fallback: JSX.Element }>) {
  const router = useRouter();
  const { status, data: signInCheckResult } = useSigninCheck();
  const { data: user } = useUser();

  if (status === "loading") {
    return props.fallback;
  }
  if (signInCheckResult?.signedIn === true) {
    return props.children as JSX.Element;
  } else {
    return router.push("/login");
  }
}

export default function App({
  Component,
  pageProps
}: MyAppProps) {
  return (
    <FirebaseAppProvider firebaseConfig={firebaseConfig}>
      <AuthWrapper>
        <AuthGuard fallback={<PageLoader />}>
          <TeamProvider>
            <Component {...pageProps} />
          </TeamProvider>
        </AuthGuard>
      </AuthWrapper>
    </FirebaseAppProvider>
  );
}

In my page component, I try to retrieve the user, but on the initial render the user is undefined. This results in useIdtokenResult throwing an error (Error: you must provide a user) since no user is given and that is required. I need useIdTokenResult because the component needs those values at a later stage.

export default function EditRouteForm() {
  const { data: user } = useUser();
  const { data: idTokenResult } = useIdTokenResult(user as User, false, {
    initialData: { claims: { group: "", roles: [] } },
  });
  const { group, roles } = idTokenResult.claims
 /* ...... */
}

Expected behavior

Because of my auth guard in the AuthCheck component, I'm expecting useUser to always return an user.

Actual behavior

The initial render of useUser always returns undefined.

@jhuleatt
Copy link
Collaborator

Hi @wieringen, does this happen with ReactFire 4.2.2? 4.2.3 released a change to useUser, and I'm wondering if we may have broken something.

We have a test to guard against this exact issue, so I'm a little surprised it is happening at all:

it('synchronously returns a user if one is already signed in', async () => {
await signIn();
const { result } = renderHook(() => useUser(), { wrapper: Provider });
expect(getAuth(app).currentUser).not.toBeNull();
expect(result.current.data).toEqual(getAuth(app).currentUser);
});

@jhuleatt
Copy link
Collaborator

Actually, I wonder if this could be related to mixing userUser and useSigninCheck. They should both agree, but I don't think we test for it, so that could be the issue. Could you please try logging the results of both in your AuthGuard component to see if they ever disagree?

Something like:

const { status, data: signInCheckResult } = useSigninCheck();
const { data: user } = useUser();

if (status === "loading") {
  return props.fallback;
}

console.assert(user === signInCheckResult.user, user, signInCheckResult.user);

@wieringen
Copy link
Author

wieringen commented Jul 4, 2023

I'm using the latest version 4.2.3. I did some more testing based on your suggestions. When I change the AuthGuard to the code below it works. Notice how the user variable is unused. When I leave out the useUser hook, I get the error in my Page component again.

function AuthGuard(props: React.PropsWithChildren<{ fallback: JSX.Element }>) {
  const router = useRouter();
  const { status, data: signInCheckResult } = useSigninCheck();
  const { data: user } = useUser();

  if (status === "loading") {
    return props.fallback;
  }
  if (signInCheckResult?.signedIn === true) {
    return props.children as JSX.Element;
  } else {
    return router.push("/login");
  }
}

@jthetzel
Copy link

jthetzel commented Jul 4, 2023

We had a similar issue with useUser() in 4.2.3 returning on initial load:

{ 
  data: undefined, 
  status: 'loading',
  ...
}

Pinning to 4.2.2 resolved the error, with useUser() returning on initial load:

{ 
  data: {
    emailVerified: true,
    ...
  }, 
  status: 'success',
  ...
}

@das-monki
Copy link

Had the same issue, user was undefined on initial render. What solved it for me was adding suspense=true at <FirebaseAppProvider suspense=true />.

See also following comment at the (deprecated) AuthCheck:

Meant for Concurrent mode only (`<FirebaseAppProvider suspense=true />`).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants