Home > Software design >  How to persist user data in Firebase/React?
How to persist user data in Firebase/React?

Time:04-12

How do I persist a user across my app? Every time I create a user via my sign up page, if I reload the page - the user is no longer logged in. I have looked into persistence on the official documentation but am still not sure when (or how) to use persistence functions VS. the onAuthStateChange function.

This signup page is just a simple email, password, confirm password form. Once the user submits their data, they are redirected via react-router-dom to a dashboard page. And once again, the user data renders once, but on page reload disappears.

/Signup:

import React, {useRef, useState, useEffect} from 'react'
import {Link, Navigate, useNavigate} from 'react-router-dom'

import { useAuth } from '../contexts/AuthContext'

import { auth } from '../firebase'

export default function 
Signup() {

  //Email Input
  const emailRef = useRef()
  const [email, setEmail] = useState("")
  const [emailFlag, setEmailFlag] = useState(false)

  //Password Input
  const passwordRef = useRef()
  const [password, setPassword] = useState("")
  const [passwordFlag, setPasswordFlag] = useState(false)

  //Password Confirmation Input
  const passwordConfirmRef = useRef()
  const [passwordConfirm, setPasswordConfirm] = useState("")
  const [passwordMatchFlag, setPasswordMatchFlag] = useState()
  
  //Destructure Authorization Functionality
  const { currentUser, signup } = useAuth()

  const navigate = useNavigate()
  

  useEffect(() => {

   verify()

  },[password, passwordConfirm])

  //Verify fields are passing
  const verify = () => {

    //set default flag values
    setPasswordFlag(false)
    setPasswordMatchFlag(false)

    //Verify length
    if(passwordRef.current.value.length >= 6) {
      setPasswordFlag(true)
    }
    //Verify match
    if(passwordRef.current.value === passwordConfirmRef.current.value) {
      setPasswordMatchFlag(true)
    }
  }

const handleSubmit = () => {

  //if values pass
  if(passwordFlag === true && passwordMatchFlag === true){
    
    signup(auth, email,password)
      .then(console.log('successfuly signed up'))
      .then(console.log(currentUser))
      .then(navigate('/dashboard'))
    
  }
}

  return (

    <div className="container mx-auto flex flex-col items-center p-4 bg-gray-200 gap-y-4">

      <h1 className='text-2xl'>Signup</h1>

        <h1 className='text-md'>Email</h1>
        <input type="email" placeholder='[email protected]' className='h-8 p-1 w-3/4 lg:w-1/2 shadow-sm' 
        value={email} onChange={(e) => setEmail(e.target.value)} ref={emailRef}></input>
        
        <h1 className='text-md'>Password</h1>
        {!passwordFlag && <p className='text-sm'>Password must be atleast 6 characters</p>}
        <input type="password" className='h-8 w-3/4 lg:w-1/2 shadow-sm' 
        value={password} onChange={(e) => setPassword(e.target.value)} ref={passwordRef}></input>
        
        <h1 className='text-md'>Confirm Password</h1>
        {!passwordMatchFlag && <p>passwords must match</p>}
        <input type="password" className='h-8 w-3/4 lg:w-1/2 shadow-sm'
        value={passwordConfirm} onChange={(e) => setPasswordConfirm(e.target.value)} ref={passwordConfirmRef}></input>

        <button onClick={handleSubmit}
        className='bg-red-200 py-1.5 w-3/4 px-4 text-center shadow-lg rounded-lg lg:w-24'>Submit</button>

    </div>    

  )
}

/AuthContext

import React, {useContext, useState, useEffect, createContext} from 'react'

import {auth} from '../firebase'
import { createUserWithEmailAndPassword, onAuthStateChanged, setPersistence } from 'firebase/auth'

const AuthContext = createContext()

export function useAuth() {
    return useContext(AuthContext)
}

export function AuthProvider({children}) {

    const [currentUser, setCurrentUser] = useState()

    onAuthStateChanged(auth, (user) => {
        setCurrentUser(user)
    })

    async function signup(auth, email, password){
        await createUserWithEmailAndPassword(auth, email, password)
        console.log('user created')
    }

    const value= {
        
        currentUser,
        signup
    }

  return (
    <AuthContext.Provider value={value}>
        {children}
    </AuthContext.Provider>
  )
}

/Dashboard

import React, {useState, useEffect} from 'react'

import { useAuth } from '../contexts/AuthContext'

export default function Dashboard() {

  
  const {currentUser} = useAuth()  

  return (
    <div>{currentUser.email}</div>
  )
}

CodePudding user response:

In your /AuthContext you can go ahead and add currentUser to your imports from firebase/auth like this: import { createUserWithEmailAndPassword, onAuthStateChanged, setPersistence, currentUser } from 'firebase/auth'

Then inside of AuthProvider you will want to load in the currentUser when the page first loads. We can use the newly imported currentUser inside of a useEffect to accomplish this, like so:

export function AuthProvider({children}) {

    const [currentUserState, setCurrentUserState] = useState() // renamed from currentUser and setCurrentUser to avoid naming collision with currentUser import

    useEffect(() => setCurrentUser(currentUser), []); // loads the persisted firebase user on mount

    ...

By loading the user this way, we can retrieve the user data even across page loads since when using firebase/auth, "the default behavior is to persist a user's session even after the user closes the browser". docs

Since the functions and data provided by firebase/auth are accessible anywhere, you may also want to consider not using /AuthContext at all. You should be able to use firebase/auth in much the same way that you would typically use a React Context.

  • Related