skip to Main Content

Simplified code that I am using:

import { useState, useEffect, useContext } from 'react'
import { useRouter } from 'next/router'

import { firestore } from './firebase-config'
import { getDoc, doc } from 'firebase/firestore'

export default function HomePage() {
  const router = useRouter()
  const user = useContext(AuthContext) // contains user object -> user.user 

  const [loading, setLoading] = useState(true)

  useEffect(() => {
    const getData = async() => {
      setLoading(true)
      const uid = user.user.uid // uid of user in firebase auth
      const id = router.query.id // id param of url

      const docRef = doc(firestore, `...`) 
      // doc in a collection that references the above uid and id
      const docSnap = await getDoc(docRef)
      // get the document from firestore

      if (docSnap.exists()) {
        importData(docSnap.data()) // add data to store to re-render page
        setLoading(false)
      } else {
        router.push('/main') 
        // if the user isn't logged in to return to '/'
          // ^^ not sure how to do these separately
        // if the user is logged in but the document does not exist to return to '/main'
      }
    }
    getData()
  }, [router.query, user.user])

  return (
    <>
      {/*  */}
    </>
  )
}

I need to load the document associated with the user’s uid and the id param of the currently loaded page, i.e. /main/[id].
These retrieve a Firestore document that is then inserted into the store which causes the HomePage function to re-render to show the data.

uid is found in user.user.uid which is set via onAuthStateChanged in app.js

id is found in router.query.id which is set via useRouter() at the top level

The useEffect() above works, but only temporarily, soon after the data is loaded and the component re-renders, I am linked to '/main' as initially uid and id start as undefined meaning that on the first run of the useEffect hook the else condition is run, it then re-runs as the user and router object is retrieved to load the data, but by the time that has occurred the page is transitioned to './main'.

Would greatly appreciate some help to make this function work.

Additionally, the user should go back to './main' if the document doesn’t exist but they are logged in, and if they are not logged in to then be returned to the root ('./')

Thanks in advance!

2

Answers


  1. Chosen as BEST ANSWER

    So I have managed to fix the issue:

    To be able to use uid in the useEffect() hook, onAuthStateChanged is called again rather than using the AuthContext that is created at the top level as this will wait until the user exists

    To wait for router.query to be updated you can call router.isReady which returns a Boolean value on whether it has been updated.

    Using both of these in this way:

    useEffect(() => {
      onAuthStateChanged(auth, async (user) => {
        if (user) {
          if (router.isReady) {
            // do stuff -> user exists
          } else {
            // user exists but the document does not 
            router.push('/main')
          }
        }
        else {
          // user is not logged in 
          router.push('/')
        }
      })
    }, [router.isReady, router.query])
    

  2. You can add a loading state for the document retrieval in addition to the loading state that you already have to make sure that the document retrieval is completed before navigating away from the page.

    import { firestore } from './firebase-config'
    import { getDoc, doc } from 'firebase/firestore'
    
    export default function HomePage() {
      const router = useRouter()
      const user = useContext(AuthContext) // contains user object -> user.user 
    
      const [loading, setLoading] = useState(true)
      const [docLoading, setDocLoading] = useState(true)
    
      useEffect(() => {
        const getData = async() => {
          setLoading(true)
          const uid = user.user.uid // uid of user in firebase auth
          const id = router.query.id // id param of url
    
          if (!uid) {
            setLoading(false)
            router.push('/')
            return
          }
    
          if (!id) {
            setLoading(false)
            router.push('/main')
            return
          }
    
          const docRef = doc(firestore, `...`) 
          // doc in a collection that references the above uid and id
          setDocLoading(true)
          const docSnap = await getDoc(docRef)
          // get the document from firestore
          setDocLoading(false)
    
          if (docSnap.exists()) {
            importData(docSnap.data()) // add data to store to re-render page
            setLoading(false)
          } else {
            router.push('/main')
          }
        }
        getData()
      }, [router.query, user.user])
    
      if (loading || docLoading) {
        return <div>Loading...</div>
      }
    
      return (
        <>
          {/* render your component here */}
        </>
      )
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search