将 react-router 与 firebase 身份验证结合使用

时间:2021-02-06 05:05:20

标签: reactjs firebase react-router

我的主要 App 组件跟踪当前通过 firebase onAuthStateChanged 回调登录的用户,然后我可以使用它来将用户重定向到 /login 路由,如果用户对象为空。这工作正常,但是如果您在登录页面上导航到不同的路由,您不会被重定向回来,这会导致错误,因为其他路由要求您登录才能正常运行。代码如下:

export function App() {

    const auth = firebase.auth();
    const [user, setUser] = useState(null);

    useEffect(()=>{
        auth.onAuthStateChanged(()=> {
            setUser(auth.currentUser);
        })
    }, []);


    return (
        <BrowserRouter>
            <Switch>
                <Route path="/login" exact component={LoginPage}/>
                <Route path="/" exact component={HomePage}/>
                {!user ? <Redirect to="/login"/> : null}
            </Switch>
        </BrowserRouter>
    );
}

我已尝试将 !user ? <Redirect to="/login"/> 移至 Switch 组件的顶部,但这只会使您每次刷新页面时都退出。关于如何解决这个问题的任何想法?谢谢。

2 个答案:

答案 0 :(得分:2)

为什么不重构您的 Route 元素以拥有私有路由器和公共路由?私有路由将是那些需要身份验证的路由,而公共路由则不需要它。当有人试图在未经身份验证的情况下访问私有路由时,他们将被自动送走。

创建一个名为 PrivateRoute 的元素并将您的 Firebase 身份验证放入其中。示例:

const PrivateRoute = ({children, ...props}) => {
    const auth = firebase.auth();
    const [user, setUser] = useState(null);

    useEffect(()=>{
        auth.onAuthStateChanged(()=> {
            setUser(auth.currentUser);
        })
    }, []);

    return (
        <Route {...props} render={() => {
            return valid === null ?
                <div>Some kind of loader/spinner here...</div>
                :
                user ?
                    children
                :
                    <Redirect to='/login' />
        }} />
    )
}

然后在您的 App 中,像这样使用它:

    return (
        <BrowserRouter>
            <Switch>
                <PrivateRoute exact path="/">
                    <HomePage />
                </PrivateRoute>
                <Route exact path="/login" component={LoginPage} />
            </Switch>
        </BrowserRouter>
    );

如果未通过身份验证,这会将尝试访问 / 的任何人重定向到 /login

稍后您创建的任何路由如果需要身份验证,都可以像这样包装。

答案 1 :(得分:0)

我正在使用以下方法并且效果很好(只需复制我现有的有效项目):

import React, {useState, useEffect} from 'react'
import {BrowserRouter as Router, Switch, Route, Redirect} from "react-router-dom"
import {connect} from "react-redux"
import useAuth from './hooks/useAuth'

import styles from './styles.js'

import Landing from './components/Landing'
import Login from './components/Login'
import Logout from './components/Logout'
import Post from './components/Post'
import Users from './components/Users'
import User from './components/User'
import Signup from './components/Signup'
import Profile from './components/Profile'
import AddSocieta from './components/AddSocieta'
import Constructor from './components/Constructor'

const mapStateToProps = state => ({
  ...state
});

function ConnectedApp() {

  const [dimension, setDimention] = useState({windowWidth: 0, windowHeight: 0})

  const currentStyles = {
    ...styles,
    showFooterMenuText: styles.showFooterMenuText(dimension),
    showSidebar: styles.showSidebar(dimension),
    topMenuCollapsed: styles.topMenuCollapsed(dimension),
    topMenuHeight: styles.topMenuHeight(dimension),
    paddingLeftRight: styles.paddingLeftRight(dimension),
    fullScreenMenuFontSize: styles.fullScreenMenuFontSize(dimension),
    showSubLogoText: styles.showSubLogoText(dimension),
    roundedImageSize: styles.roundedImageSize(dimension)
  };

  const [auth, profile] = useAuth()

  const [isLoggedIn, setIsLoggedIn] = useState(false)

  useEffect(() => {

    if (auth && auth.uid) {
      setIsLoggedIn(true)
    } else {
      setIsLoggedIn(false)
    }

    updateDimensions();
    window.addEventListener("resize", updateDimensions);

    return function cleanup() {
      window.removeEventListener("resize", updateDimensions);
    }
  }, [auth, profile]);

  function updateDimensions() {
    let windowWidth = typeof window !== "undefined"
      ? window.innerWidth
      : 0;
    let windowHeight = typeof window !== "undefined"
      ? window.innerHeight
      : 0;

    setDimention({windowWidth, windowHeight});
  }

  return (<Router>
    <Redirect to="/app/gare"/>
    <div className="App">
      <Switch>
        <Route path="/constructor"><Constructor styles={currentStyles}/></Route>
        <Route path="/post"><Post/></Route>
        <Route path="/login"><Login styles={currentStyles}/></Route>
        <Route path="/logout"><Logout styles={currentStyles}/></Route>
        <Route path="/users"><Users styles={currentStyles}/></Route>
        <Route path="/user/:uid"><User styles={currentStyles}/></Route>
        <Route path="/app"><Landing styles={currentStyles}/></Route>
        <Route path="/signup" render={isLoggedIn
            ? () => <Redirect to="/app/gare"/>
            : () => <Signup styles={currentStyles}/>}/>
        <Route path="/profile" render={isLoggedIn
            ? () => <Profile styles={currentStyles}/>
            : () => <Redirect to="/login"/>}/>

          <Route path="/add-societa"><AddSocieta styles={currentStyles}/></Route>
      </Switch>
    </div>
  </Router>);
}

const App = connect(mapStateToProps)(ConnectedApp)

export default App;