【问题标题】:Using if statement to determine if a Firebase User can access certain React Router paths使用 if 语句确定 Firebase 用户是否可以访问某些 React Router 路径
【发布时间】:2021-02-11 02:53:54
【问题描述】:

我有一个通过 React Router 定义路由的 React Web 应用。

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

为了判断用户是否登录,我使用了以下函数:

let user = firebase.auth().currentUser;

    if (user) {

    } else {

    }
  }

从那里,我认为我可以将“受保护”路由放入初始位置,如果这样可以确定用户是否已登录,如下所示:

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

我知道逻辑有缺陷,但有没有办法可以重构我的代码来实现这个非常简单的 hack 以使用 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;

【问题讨论】:

    标签: javascript reactjs firebase firebase-authentication


    【解决方案1】:

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

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

    简单的选择:

    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-router 有一个 &lt;Redirect to={path}/&gt; 组件,非常适合用于检查经过身份验证的用户等用例。

    清洁工(也是我的首选):

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

    • base.js是我们要初始化firebase的地方

      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
    • 我们还将currentUser 中的currentUser 作为道具传递,因此它可以在该组件中使用。

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

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

    【讨论】:

    • 这是一个很好的答案。所以最终的代码 sn-p 就是我的 app.js 中的代码(那是我当前的路由所在的位置,假设我沿着上下文路由走)?我的其余链接会保持不变吗?像我的登录/注册等怎么样?
    • 没错,所以您可以像 Route 一样使用 PrivateRoute (如您如何指示路径并声明要呈现的组件)作为您想要进行身份验证的组件和普通的 Route从“react-router-dom”获取您希望每个人都可以访问的组件。
    • 再次感谢您的回复!我已经尝试实现代码......不幸的是我收到了这个错误:TypeError:赋值的右侧不能被解构。我会更新我原来的问题,但你能帮忙吗?
    • 当然,您是否像这样导入 useContext 挂钩:import React, { useContext } from 'react';
    • 再次感谢您的回复!不幸的是,我的 PrivateRoute.js 文件中仍然出现同样的错误。我试过用谷歌搜索错误以查看解构以及如何更改它,但我觉得我对此了解不够。虽然我热衷于使用这个解决方案,因为它比我的想法干净得多。
    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-10-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-03
      • 2012-10-10
      • 1970-01-01
      • 2017-03-21
      相关资源
      最近更新 更多