使用if语句来确定Firebase用户是否可以访问某些React Router路径



我有一个React web应用程序,通过React Router定义路由。

应用程序上的身份验证是用Firebase完成的,我想根据用户是否登录来保护某些路由。

为了确定用户是否登录,我使用了以下功能:

let user = firebase.auth().currentUser;
if (user) {
} else {
}
}

从那时起,我想我可以把"受保护"的路由放在初始if中,因为这将决定用户是否登录,就像这样:

let user = firebase.auth().currentUser;
if (user) {
<div>
<Route path="/dashboard" component={Dashboard} exact />
</div>
} else {
<Route path="/sign-in" component={SignIn} />
}
}

如果用户没有登录,那么他们将被重定向到登录组件。

然而,这不起作用。仪表板页面只是返回为一个空白的白色屏幕,而不是重定向。

事实上,控制台中显示了一条错误消息:

警告:失败的道具类型:提供给Switch的道具children无效,应为ReactNode。

为了参考,这里是我的完整组件:

class App extends Component {
constructor(props) {
super(props)
this.userLoggedIn = this.userLoggedIn.bind(this)

}

userLoggedIn = () => {
let user = firebase.auth().currentUser;
if (user) {
<div>
<Route path="/dashboard" component={Dashboard} exact />
</div>
} else {
<Route path="/sign-in" component={SignIn} />
}
}
render() {
return (
<div>
<Switch>
{this.userLoggedIn}
<Route exact path="/" component={Homepage}  />
<Route path="/sign-in" component={SignIn} />
<Route path="/register" component={Register} />
</Switch>
</div>
)
}
}

我知道这个逻辑是有缺陷的,但有没有一种方法可以重构我的代码来实现这个非常简单的破解,使用Firebase Auth获得受保护的路由?

希望这很清楚,如果需要更多信息,请告诉我。

编辑:根据答案编辑了我的代码,但现在收到以下错误消息:

TypeError: Right side of assignment cannot be destructured
PrivateRoute
src/PrivateRoute.js:8
5 | import { AuthContext } from './Auth'
6 | 
7 | export default function PrivateRoute({ component: RouteComponent, ...rest }) {
>  8 |     const {currentUser} = useContext(AuthContext)
9 |     return (
10 |         <Route
11 |         {...rest}

我的更新代码。。。

Auth.js

import React, { useEffect, useState } from 'react'
import * as firebase from "firebase/app";
import "firebase/auth";
import "firebase/firestore";
import firebaseConfig from './firebaseConfig';

export const AuthContext  = React.createContext();
export function AuthProvider({ children }) {
const  [currentUser, setCurrentUser] = useState(null);
useEffect(() => {
firebase.auth().onAuthStateChanged(setCurrentUser);
}, []);
return (
<AuthContext.Provider
value={{currentUser}}
>
{children}
</AuthContext.Provider>
)
}

PrivateRoute.js

import React, { useContext } from 'react';
import { Route, Redirect } from 'react-router-dom';
import { AuthContext } from './Auth'
export default function PrivateRoute({ component: RouteComponent, ...rest }) {
const { currentUser } = useContext(AuthContext)
return (
<Route
{...rest}
render={routeProps =>
!!currentUser ? (
<RouteComponent {...routeProps} currentUser={currentUser} />
) : (
<Redirect to={'/signin'}/>
)
}
/>
)
}

App.js

import React, { Component } from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import PrivateRoute from './PrivateRoute';
import './App.css';
import * as firebase from "firebase/app";
import "firebase/auth";
import "firebase/firestore";
import firebaseConfig from './firebaseConfig';

class App extends Component {
render() {
return (
<div>
<Switch>
<PrivateRoute path="/dashboard" component={Dashboard} />
<Route exact path="/" component={Homepage}  />
<Route path="/sign-in" component={SignIn} />
<Route path="/register" component={Register} />
</Switch>
</div>
)
}
}
export default App;

有两种方法可以解决这个问题并稍微清理一下。

假设您只希望经过身份验证的用户访问仪表板页面

简单的选择:

class App extends Component {

state = {
currentUser: undefined
}
componentDidMount(){
const user = firebase.auth().currentUser
this.setState({currentUser: user})
}
render() {
return (
<div>
<Switch>
<Route exact path="/dashboard">
{this.state.user ? (
<Dashboard />
) : (
<Redirect to={"/login"} />
)}
</Route>
</Switch>
</div>
)
}
}

几个注意事项:

  • 当使用componentDidMount()装载组件时,最好获取当前用户并设置任何初始状态
  • React支持的不是使用构造函数,而是简单地声明初始状态
  • 最后,react路由器有一个<Redirect to={path}/>组件,它非常适合检查已验证用户等用例

清洁剂(也是我的首选(:

如果你想抽象身份验证过程,让你的生活从长远来看更简单,你可以创建2个文件(例如base.jsAuth.jsPrivateRoute.js(

  • base.js是我们初始化火球的地方

    import * as firebase from 'firebase/app'
    const app = firebase.initializeApp({
    apiKey: process.env.REACT_APP_FIREBASE_KEY,
    authDomain: process.env.REACT_APP_FIREBASE_DOMAIN,
    databaseURL: process.env.REACT_APP_FIREBASE_DATABASE,
    projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
    storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
    messagingSenderId: process.env.REACT_APP_FIREBASE_SENDER_ID,
    measurementId: process.env.REACT_APP_MEASUREMENT_ID
    });
    export default app;
    

注意:

  • 我将process.env......作为API密钥的值,因为将密钥保存在.env文件中始终是最佳做法。

  • Auth.js是当身份验证状态改变时,我们将处理获得身份验证用户的位置

Auth.js:

import React, { useEffect, useState } from 'react'
import app from './base'
export const AuthContext  = React.createContext();
export function AuthProvider({ children }) {
const  [currentUser, setCurrentUser] = useState(null);
useEffect(() => {
app.auth().onAuthStateChanged(setCurrentUser);
}, []);
return (
<AuthContext.Provider
value={{currentUser}}
> 
{children}
</AuthContext.Provider>
)
}

注意:

  • 别忘了导入React、useState、useEffect和firebase
  • 该文件将提供一个身份验证上下文,以便您可以从React应用程序中的任何位置获取它

PrivateRoute.js:

import React, { useContext } from 'react';
import { Route, Redirect } from 'react-router-dom';
import { AuthContext } from './Auth'
export default function PrivateRoute({ component: RouteComponent, ...rest }) {
const currentUser = useContext(AuthContext)
return (
<Route
{...rest}
render={routeProps =>
!!currentUser ? (
<RouteComponent {...routeProps} currentUser={currentUser} />
) : (
<Redirect to={'/login'}/>
)
}
/>
)
}

注意:

  • 在这里,我们从Auth.js文件导入身份验证上下文,并获取当前用户
  • 然后我们创建一个PrivateRoute,它检查是否有当前用户,如果有,使用react-router-dom包中的RouteRedirect,如果用户未通过身份验证,我们可以将其重定向到/login页面,或者重定向到与其他道具一起传递的RouteComponent(如果有(
  • 我们还在RouteComponent中传递currentUser作为道具,以便它在该组件中可用

有了这个,您现在可以像这样使用PrivateRoute组件,以确保只有经过身份验证的用户才能使用给定的组件:

import PrivateRoute from "./PrivateRoute";
import Dashboard from "./Dashboard"
<PrivateRoute exact path="/dashboard" component={Dashboard} />

this.userLoggedIn是一个函数,因此它必须返回您希望它呈现的JSX:

userLoggedIn = () => {
let user = firebase.auth().currentUser;
if (user) {
return (<div>
<Route path="/dashboard" component={Dashboard} exact />
</div>);
} else {
return <Route path="/sign-in" component={SignIn} />;
}
}

此外,在渲染函数中,您希望调用函数this.userLoggedIn,而不仅仅是将其作为值放入。否则它什么都不会做:

render() {
return (
<div>
<Switch>
{this.userLoggedIn()}
<Route exact path="/" component={Homepage}  />
<Route path="/sign-in" component={SignIn} />
<Route path="/register" component={Register} />
</Switch>
</div>
)
}

最新更新