受保护路由,上下文API和firebase用户身份验证请求的问题

Wil*_*iam 17 javascript firebase reactjs firebase-authentication react-context

我正在编写一个使用Firebase进行身份验证的基本CRUD React应用程序.目前我正在尝试为名为Dashboard的组件创建受保护的路由.受保护的路由确保除非用户通过身份验证,否则任何封装的路由(例如仪表板)都不会呈现.如果用户未经过身份验证,则路由器会重定向到目标网页.

我完成此任务的方式是以这篇文章为蓝本的:

我已经模仿了上面文章中的模式,它工作正常.当我合并firebase(特别是firebase身份验证)时,即使用户已登录,我的应用也不会呈现仪表板组件.而只是重定向到登陆页面

我知道问题是什么(我认为),但我不确定如何解决它.

问题是对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;


Run Code Online (Sandbox Code Playgroud)

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};

Run Code Online (Sandbox Code Playgroud)

PageComponents /仪表板

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

Run Code Online (Sandbox Code Playgroud)

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;




Run Code Online (Sandbox Code Playgroud)

服务/认证

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 }


Run Code Online (Sandbox Code Playgroud)

Shu*_*tri 11

问题:您确实是正确的,API调用getUser是异步的并且被触发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}
Run Code Online (Sandbox Code Playgroud)

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;
Run Code Online (Sandbox Code Playgroud)