Inside of useEffect, both condition branches got triggered when refresh page
Asked Answered
L

2

0

This is the useEffect code in the React component.

  import { useSession } from '@supabase/auth-helpers-react'
  const session = useSession()
  useEffect(() => {
    if (session) {
      console.debug('stay in page')
    } else {
      console.debug('goto login')
      router.push('/login').then(
        () => {
          console.debug('goto login done')
        }
      )
    }
  }, [session, router])

It uses a supabase to manage auth (session).

It has a very weid issue when refresh page, (login, logout redirect through router has no issues at all, only when Refresh page.), both branches will be reached, so in js console, we could see logs from both condition branches.

'stay in page' and goto login/ goto login done.

I assume it is due to session changed after refresh. 1st time is undefined, to trigger the 2nd brach router.push, immediately, when session be found, triggers the 1st branch stay in page.

Is there any suggestion to bypass it? Or any good practice to handle page refresh in React/NextJS? Or anyone has similar or the same issue before?

Lager answered 13/6, 2023 at 10:56 Comment(2)
Can you show the code for how session is being fetched?Sillabub
this is the lib for useSession. import { useSession, useSupabaseClient } from '@supabase/auth-helpers-react'Lager
L
0

Finally, I got the root cause for this.

It is from the useSession hook from supabase auth helper, it is an async function, and it does not include the state of whether it is loading or error, which it means it will always be null in the beginning (which it means it is in loading).

The simple solution to solve it, is to use useSessionContext directly instead. session will still be gotten in an async way, but isLoading and error state can be gotten in a sync way to resolve this issue, and they can be used for condition branching inside useEffect.

New code will be like this:

  import { useSessionContext } from '@supabase/auth-helpers-react'
  const {isLoading, session} = useSessionContext()
  useEffect(() => {

    if (isLoading) {
      console.debug('show loading page')
    }
    else if (session) {
      console.debug('stay in page')
    } 
    else {
      console.debug('goto login')
      router.push('/login').then(
        () => {
          console.debug('goto login done')
        }
      )
    }
  }, [session, isLoading, router])

Lager answered 13/6, 2023 at 23:10 Comment(0)
B
1

In your code, useEffect will be executed whenever value for session or router changes (the 2nd parameter to useEffect). Hence, both if and else are not being executed in the same run of useEffect, albeit, in two different runs that are executed perhaps in quick succession.

Here is one way of achieving what you are after.

  1. Create and manage the state of a variable using useState
  2. In the useEffect, set the state (or not) by applying logic to check session
  3. In the return either render the current page or show the login page.

A sample implementation:

const App = (props) => {
  const [isLoggedIn, setIsLoggedIn] = useState(false);
  
  useEffect(()=>{
    //apply the logic and set the state
    if(logic) setIsLoggedIn(true);
  },[]) 
  
  return  {isLoggedIn ? <ActiveApp /> : <Login />}

}
Bond answered 13/6, 2023 at 12:30 Comment(1)
Thanks for the help, I finally figured out why. Actually, put a new state does not help, because the issues is from the logic of checking session exist or not. Because session changed, and there is no way to track the state of whether session is ready, so it will be null in the beginning all the time. Which trigger the both branches in useEffect in in quick succession. Thanks for the example anyway.Lager
L
0

Finally, I got the root cause for this.

It is from the useSession hook from supabase auth helper, it is an async function, and it does not include the state of whether it is loading or error, which it means it will always be null in the beginning (which it means it is in loading).

The simple solution to solve it, is to use useSessionContext directly instead. session will still be gotten in an async way, but isLoading and error state can be gotten in a sync way to resolve this issue, and they can be used for condition branching inside useEffect.

New code will be like this:

  import { useSessionContext } from '@supabase/auth-helpers-react'
  const {isLoading, session} = useSessionContext()
  useEffect(() => {

    if (isLoading) {
      console.debug('show loading page')
    }
    else if (session) {
      console.debug('stay in page')
    } 
    else {
      console.debug('goto login')
      router.push('/login').then(
        () => {
          console.debug('goto login done')
        }
      )
    }
  }, [session, isLoading, router])

Lager answered 13/6, 2023 at 23:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.