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.