我有一个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.js
、Auth.js
和PrivateRoute.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
包中的Route
和Redirect
,如果用户未通过身份验证,我们可以将其重定向到/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>
)
}