我正在编写一个使用Firebase进行身份验证的基本CRUD React应用。目前,我正在尝试为名为 Dashboard 的组件创建一条受保护的路由。受保护的路由可确保除非经过用户认证,否则任何封装的路由(例如仪表板)都不会呈现。如果未通过认证,则路由器将重定向到目标页面。
我完成此操作的方式以this文章为模型:
我已经在上面的文章中模拟了这种模式,并且效果很好。当我合并Firebase(特别是Firebase身份验证)时,即使用户登录,我的应用程序也不会呈现 Dashboard 组件。它只是重定向到着陆页
我知道问题是什么(我认为),但不确定如何解决。
问题在于,对firebase的调用是异步操作,并且仪表板尝试在解决对firebase的调用之前尝试加载。
我想知道它们是否对我的代码有任何调整,我可以进行修复。
我可以每次 一次对Firebase进行api调用,用户加载受保护的路由(以检查身份验证),但我希望在Context状态下设置身份验证并引用该状态直到用户登录或注销为止。
我在下面放置了相关代码。所有文件都在src
目录中
谢谢!
App.js
import React, { Component } from 'react';
import { BrowserRouter, Route, Redirect } from "react-router-dom";
import {Switch} from 'react-router';
import Landing from './PageComponents/Landing';
import {Provider} from './PageComponents/Context';
import Dashboard from './PageComponents/Dashboard';
import ProtectedRoute from './PageComponents/ProtectedRoute';
class App extends Component {
render() {
return (
<div className="App">
<Provider>
<BrowserRouter>
<div>
<Switch>
<Route exact={true} path="/" component={Landing} />
<ProtectedRoute exact path="/dashboard" component={Dashboard} />
</Switch>
</div>
</BrowserRouter>
</Provider>
</div>
);
}
}
export default App;
PageComponents / Context.js
import React from 'react';
import { getUser } from '../services/authentication';
let Context = React.createContext();
class Provider extends React.Component {
state = {
userID: true,
user:undefined,
authenticated:false
}
async getUser(){
try{
let user = await getUser();
return user
} catch(error){
console.log(error.message)
}
}
async componentDidMount(){
console.log("waiting to get user")
let user = await this.getUser();
console.log(user)
console.log("got user")
this.setState({
userID: user.uid,
user:user,
authenticated:true
})
}
render(){
console.log(this.state)
return(
<Context.Provider value={{
state:this.state
}}>
{this.props.children}
</Context.Provider>
)
}
}
const Consumer = Context.Consumer;
export {Provider, Consumer};
PageComponents / Dashboard
import * as React from 'react';
import {Consumer} from '../../PageComponents/Context';
class Dashboard extends React.Component {
render(){
console.log("Dashboard component loading....")
return(
<Consumer>
{(state)=>{
console.log(state)
return(
<div>
<p> Dashboard render</p>
</div>
)
}}
</Consumer>
)
}
}
export default Dashboard
PageComponents / ProtectedRoute
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
import { Consumer } from '../PageComponents/Context';
const ProtectedRoute = ({ component: Component, ...rest }) => {
console.log("Middleware worked!");
return (
<Consumer>
{(context)=>{
/*________________________BEGIN making sure middleware works and state is referenced */
console.log(context);
console.log("Middle ware");
/*________________________END making sure middleware works and state is referenced */
console.log( context.getState().authenticated + " <--- is authenticated from ProtectedRoute. true or false?")
if(context.state.authenticated){
return(
<Route {...rest} render={renderProps => {
console.log(renderProps);
return (<Component {...renderProps} />)
}}/>
)
}else{
return <Redirect to="/"/>
}
}}
</Consumer>
)
};
export default ProtectedRoute;
服务/身份验证
import firebase from '../../services/firebase'
const getUser = () =>{
return new Promise((resolve, reject) => { // Step 3. Return a promise
//___________________ wrapped async function
firebase.auth().onAuthStateChanged((user)=> {
if(user){
resolve(user); //____This is the returned value of a promise
}else{
reject(new Error("Get user error"))
}
})
//_____________________END wrapped async function
});
}
export {getUser }
答案 0 :(得分:10)
问题:您确实是正确的,对getUser
的API调用是异步的,并在componentDidMount
中触发,因此在设置authentication
状态时到true
,Redirect
组件已被触发。
解决方案::您需要等到身份验证请求成功后,再决定加载路由或在ProtectedRoute
组件中重定向。
要使其正常运行,您需要加载状态。
PageComponents / Context.js
let Context = React.createContext();
class Provider extends React.Component {
state = {
userID: true,
user:undefined,
loading: true,
authenticated:false
}
async getUser(){
let user = await getUser();
return user
}
async componentDidMount(){
console.log("waiting to get user")
try {
let user = await this.getUser();
console.log(user)
console.log("got user")
this.setState({
userID: user.uid,
user:user,
loading: false,
authenticated:true
})
} catch(error) {
console.log(error);
this.setState({
loading: false,
authenticated: false
})
}
}
render(){
return(
<Context.Provider value={{
state:this.state
}}>
{this.props.children}
</Context.Provider>
)
}
}
const Consumer = Context.Consumer;
export {Provider, Consumer}
PageComponents / ProtectedRoute
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
import { Consumer } from '../PageComponents/Context';
const ProtectedRoute = ({ component: Component, ...rest }) => {
console.log("Middleware worked!");
return (
<Consumer>
{(context)=>{
/* BEGIN making sure middleware works and state is referenced */
console.log(context);
console.log("Middle ware");
/* END making sure middleware works and state is referenced */
console.log( context.getState().authenticated + " <--- is authenticated from ProtectedRoute. true or false?")
// Show loading state
if (context.state.loading) {
return <Loader />
}
if(context.state.authenticated){
return(
<Route {...rest} render={renderProps => {
console.log(renderProps);
return (<Component {...renderProps} />)
}}/>
)
}else{
return <Redirect to="/"/>
}
}}
</Consumer>
)
};
export default ProtectedRoute;