Fetch data from Firestore when the page loads using the useEffect hook

Below is the simplified code snippet I am currently 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 fetchData = 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') 
        // redirects user  to '/main' if they are not logged in; otherwise return to '/'
        
      }
    }
    fetchData()
  }, [router.query, user.user])

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

The main objective here is to load the document linked with the user's uid and the id parameter of the current webpage like /main/[id]. The fetched Firestore document is then added to the store triggering the rebuild of the HomePage function to display the information.

uid can be accessed through user.user.uid, which gets set using onAuthStateChanged in app.js

id can be retrieved via router.query.id, which is established using useRouter() at the top level

The mentioned useEffect() does its job but momentarily, as soon as the data loads and the component refreshes, it gets directed to '/main'. This occurs because initially both uid and id begin as undefined. As a result, on the first run of the useEffect hook, the else condition executes and subsequently runs again after retrieving the user and router objects to fetch the data. However, by then, the webpage transitions to './main'.

I would greatly appreciate any assistance in resolving this issue.

Additionally, if the document doesn't exist but the user is logged in, they should be taken back to './main'; if they are not logged in, they should be redirected to the root ('./')

Thank you in advance for your help!

Answer №1

To ensure that the document retrieval process is completed before moving away from the page, you can implement a loading state specifically for this task in addition to the existing loading state.

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

export default function HomePage() {
  const router = useRouter()
  const user = useContext(AuthContext) // holds 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 (
    <>
      {/* your component rendering goes here */}
    </>
  )
}

Answer №2

After troubleshooting the issue, I found a solution:

In order to access the uid in the useEffect() hook, instead of relying on the AuthContext created at the top level, it is necessary to call onAuthStateChanged again. This ensures that the user exists before proceeding.

To ensure that router.query is up-to-date, you can utilize router.isReady, which provides a boolean value indicating whether the update has occurred.

By combining these two approaches as shown below:

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

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Guide to creating a cryptosystem using a Synchronous Stream Cipher with Vue js

I am currently working with a pseudo-random number generator that creates binary numbers using a user-supplied polynomial and the LFSR method. To enhance the process, I need to convert a loaded file into binary format so that I can apply the XOR operatio ...

Adjusting the Transparency of the Background in a Pop-Up

I am experiencing an issue with my popup where I want the background to be transparent. However, when I set the opacity in CSS to 0.5 or less, the text also becomes transparent and very dark. How can I achieve a background with 50% opacity while keeping th ...

Having trouble with implementing the .addclass function in a dice roller project

I'm looking to have the element with id=die load initially, and then on a button click event labeled "click me," apply the corresponding CSS class such as 'die1,' 'die2,' and so forth. function roll() { var die = Math.floor(Ma ...

What is the reason for the checkboxes in vuejs not being rendered with the checked attribute set

When working on an edit form, I encountered a situation where I had multiple options to choose from. These options were fetched via ajax using axios and assigned to the variable permisos in the component. Later, these options are rendered through a v-for l ...

Eliminate server-side functionality from the React project's boilerplate template

After cloning and installing the project from https://github.com/react-boilerplate/react-boilerplate, I realized that I only need the client-side portion as I intend to use a pre-existing server (express) for my application setup. Below is an excerpt f ...

A JavaScript or CSS file within an HTML document

I understand this may seem like a silly question. However, out of curiosity, is there a way to upload an HTML file (with a .html extension) as a JavaScript or CSS file (renamed with a .js or .css extension), specifying the type header as either HTML or js ...

Set up a jquery code that ensures a loading effect will only be displayed one time

After clicking on a specific icon, I dynamically add a loading message to the DOM. The issue arises when the user clicks twice, resulting in two instances of the loading message appearing simultaneously. How can I ensure that only one "loading..." message ...

How can I add text to an HTML5 SVG similar to using the HTML5 <p> tag?

I am currently working on creating dynamic rectangular boxes and I am facing some difficulties with inserting text into the shapes. The SVG text requires setting x and y coordinates in separate text tags, and doesn't have built-in width and height pro ...

How do I utilize ng-repeat in Angular to pass a variable that specifies the number of repetitions?

When working on my app, I encountered a situation where I needed to retrieve elements from a database and display them using ng-reat. Everything was going smoothly until I realized that within this list, there was another set of data that required a separa ...

Trigger a warning pop-up if a selection has not been made in a dropdown menu using jQuery

I am attempting to display an alert popup when the user fails to select a value from the dropdown menu. Below is my HTML code: <div id="reminder" class="popup-layout"> ... ... </form> </div> In my JavaScript function page, I have tried ...

Error: The variable success_msg has not been defined in the EJS Reference

I am in the process of developing a library website for my school that includes login and administration capabilities. I am relatively new to node.js and EJS, but I recently revamped the routing and page delivery system to use EJS and express. As part of u ...

Add HTML and JavaScript code dynamically with JavaScript

Currently, I am working on a project that involves an HTML table with some basic JS interactions triggered by user clicks. The structure looks something like this: htmlfile.html ... ... position action ...

Validation of Button Groups and Automatic Disabling after Initial Click with HTML, CSS, and JavaScript

Criteria: Upon clicking a button from the selection of four buttons, all other buttons should become disabled except for the Next button. An alert window must appear when the Next button is clicked without selecting any other buttons, preventing navigatio ...

Tips for extracting dynamically loaded content from a website using Node.js and Selenium?

I'm currently encountering some challenges when trying to scrape a website that utilizes react for certain parts of its content, and I'm unsure about the reason behind my inability to extract the data. Below is the HTML structure of the website: ...

Angular: Leveraging Nested Callbacks for Efficient HTTP Queries

I'm currently facing an issue with structured English. GET HTTP Resource FOR every data item received do GET another HTTP Resource Alter the original data from the outer loop with data from the inner GET RETURN altered data How can ...

Limit access to route in ExpressJS only to internal redirects

I'm managing an ExpressJS application that includes specific routes which I intend to only function when redirected to from my code, rather than input directly into the URL. Essentially, if a user attempts to enter "myapp.com/url" it should not be ac ...

Utilize node.js on your local machine and leverage gulp to monitor any modifications

I recently copied a repository from https://github.com/willianjusten/bootstrap-boilerplate and followed these steps. git clone git://github.com/willianjusten/bootstrap-boilerplate.git new_project cd bootstrap-boilerplate npm install gulp The gulp comman ...

Difficulty obtaining elements in Internet Explorer, however works fine in Chrome and Firefox

My <textarea> is set up like this: <textarea class="form-control notetext" id="{{this._id}}-notetext" name="notetext">{{this.text}}</textarea> I am using ajax to send data and load a partial webpage. After loading the content, I attemp ...

The login process in Next-auth is currently halted on the /api/auth/providers endpoint when attempting to log in with the

My Next-auth logIn() function appears to be stuck endlessly on /api/auth/providers, as shown in this image. It seems that the async authorize(credentials) part is not being executed at all, as none of the console.log statements are working. /pages/api/au ...

Include a class above the specified element; for instance, apply the class "act" to the "<ul>" element preceding the "li.item1"

Hello there! I need some assistance, kinda like the example here Add class and insert before div. However, what I really want to do is add the class "act" to a class above that matches the one below: Here's how it currently looks: <ul> ...