简体   繁体   中英

onAuthStateChanged Firebase Listener on app refresh causing private route issues

I have currently initialized a React App with firebase. Within the application, I have created an open login route and a private Home route using react-router-dom. my app.js looks like so:

App.js:

import React from 'react'
import { BrowserRouter, Switch, Route } from 'react-router-dom'
import Login from './pages/Login'
import Home from './pages/Home'
import PrivateRoute from './utils/PrivateRoute'

const App = () => {
  return (
    <BrowserRouter>
      <Switch>
        <PrivateRoute exact path='/' component={Home} />
        <Route path='/login' component={Login} />
      </Switch>
    </BrowserRouter>
  )
}

export default App

I am storing the currentUser in context using the onAuthStateChanged firebase event listener like so:

AppContext:

import { useEffect, useState, createContext } from 'react'
import { auth } from '../utils/firebase'

export const AppContext = createContext()

export const AppProvider = ({ children }) => {
  const [currentUser, setCurrentUser] = useState(null)

  useEffect(() => {
    auth.onAuthStateChanged(setCurrentUser)
  }, [])

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

when a user logins in via the login route:

login:

import React, { useState, useCallback, useContext } from 'react'
import { auth } from '../utils/firebase'
import { useHistory, Redirect } from 'react-router-dom'


function Login() {
  const [formData, setFormData] = useState({ email: '', password: '' })
  const history = useHistory()

  const handleChange = ({ target: { name, value } }) => {
    setFormData({ ...formData, [name]: value })
  }

  const handleSubmit = useCallback(
    async event => {
      event.preventDefault()
      await auth
        .createUserWithEmailAndPassword(formData.email, formData.password)
        .then(user => {
          console.log(user)
          history.push('/')
        })
        .catch(err => {
          alert(err)
        })
    },
    [history, formData.email, formData.password]
  )

  return (
    <div className='form-container sign-up-container'>
      <form className='register-form' onSubmit={handleSubmit}>
        <h1>Create Account</h1>
        <div className='social-container'>
          <div className='social'>
            <i className='fab fa-facebook-f'></i>
          </div>
          <div className='social'>
            <i className='fab fa-google-plus-g'></i>
          </div>
          <div className='social'>
            <i className='fab fa-linkedin-in'></i>
          </div>
        </div>
        <span>or use your email for registration</span>

        <input
          type='email'
          placeholder='Email'
          name='email'
          onChange={handleChange}
        />

        <input
          type='password'
          placeholder='Password'
          name='password'
          onChange={handleChange}
        />

        <button type='submit'>Sign Up</button>
      </form>
    </div>
  )
}

export default Login

the currentUser is successfully stored in context and the user is pushed into the private Home route.

the Private Route looks like so:

import React, { useContext } from 'react'
import { Route, Redirect } from 'react-router-dom'
import { AppContext } from '../context/AppContext'

const PrivateRoute = ({ component: Component, ...rest }) => {
  const { currentUser } = useContext(AppContext)
  return (
    <Route
      {...rest}
      render={routeProps =>
        !!currentUser ? (
          <Component {...routeProps} />
        ) : (
          <Redirect to={'/login'} />
        )
      }
    />
  )
}

export default PrivateRoute

The issue I'm having is that when the app refreshes, the currentUser becomes null initially and then currentUser's information loads back up. While the currentUser is null on refresh, the user is kicked from the home route and redirected to the login page. I'm wondering if anyone has any suggestions on how to prevent this from happening.

You initialize your currentUser state variable with a null value:

const [currentUser, setCurrentUser] = useState(null)

This means that currentUser is always initially null when the page is first loaded. The prior user object isn't known for sure until some time later, after it's asynchronously loaded by the Firebase SDK. Your code needs to be ready for this. If you require that a user be signed in before rendering your component, you should wait for the first time onAuthStateChanged triggers with an actual user object.

You can read more about this behavior of the Firebase Auth SDK in this blog .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM