如何在没有 AuthContext 的情况下创建私有路由

How to make a Private Route without AuthContext

我正在尝试创建一条从我的登录和注册页面到我的仪表板页面的私有路由,但是我偶然发现的所有教程和指南都需要某种 AuthContext 东西,而我没有实现我的使用 AuthContext 的身份验证过程。
我尝试了不同的方法,但其中 none 行得通,当我进入仪表板页面时,结果给我一个空白页面,我该怎么做才能使其成为私人路线?顺便说一句,使用 Firebase v9。

SignUp.js

import './Signup.css'
import React, { useState } from 'react';
import { Link, useHistory } from 'react-router-dom';
import { auth }from '../../../firebase';
import { createUserWithEmailAndPassword, onAuthStateChanged } from 'firebase/auth';
import { Typography, Container, TextField, Button, Alert } from '@mui/material';

const Signup = () => {
    const [email, setEmail] = useState('');
    const [password, setPassword] = useState('');
    const [confirmPassword, setConfirmPassword] = useState('');
    const [error, setError] = useState('');
    const [user, setUser] = useState({});
    const history = useHistory();

    onAuthStateChanged(auth, (currentUser) => {
        setUser(currentUser);
    })
    
    const signup = async (e) => {
        e.preventDefault();

        if (password !== confirmPassword) {
            return setError("Passwords do not match")
        }

        try {
            const user = await createUserWithEmailAndPassword(
                auth, 
                email,
                password
            );
            history.push("/dashboard/canvas");
        } catch (err) {
            setError(err.message);
        }
    }

    return (
        <>
            <div className="text-div">
                <Typography textAlign="center" variant="h3">Create a new account</Typography>
            </div>
            
            <Container className="cont" maxWidth="xl" sx={{ backgroundColor: "#ffffff", width: 500, height: "auto", borderRadius: 4, marginTop: 5, display: "flex", flexDirection: "column", padding: 5, }}>
                { error && <Alert severity="error">{error}</Alert> }
                <TextField label="Email" margin="dense" type="email" onChange={ (e) => {
                    setEmail(e.target.value);
                }}/>

                <TextField label="Password" margin="dense" type="password" onChange={ (e) => {
                    setPassword(e.target.value);
                }}/>

                <TextField label="Confirm Password" margin="dense" type="password" onChange={ (e) => {
                    setConfirmPassword(e.target.value);
                }}/>
                <Button onClick={signup} variant="contained" sx={{ marginTop: 2, }}>Sign Up</Button>

                <div>
                    Already have an account? <Link to="/login" style={{ color: '#000' }}>Log In</Link>
                </div>
            </Container>
        </>
    )
}

export default Signup;

Login.js

import './Login.css'
import React, { useState } from 'react';
import { Link, useHistory } from 'react-router-dom';
import { signInWithEmailAndPassword } from 'firebase/auth';
import { auth }from '../../../firebase';
import { Typography, Container, TextField, Button, Alert } from '@mui/material';


const Login = () => {
    const [email, setEmail] = useState('');
    const [password, setPassword] = useState('');
    const [error, setError] = useState('');
    const history = useHistory();

    const login = async () => {
        try {
            const user = await signInWithEmailAndPassword(
                auth, 
                email,
                password
            );
            //alert("Success, user is recognized");
            history.push("/dashboard/canvas");
        } catch (err) {
            setError("The email or password you entered is incorrect");
        }
    }

    return (
        <>
            <div className="text-div">
                <Typography textAlign="center" variant="h3">Login</Typography>
            </div>
            
            <Container className="cont" maxWidth="xl" sx={{ backgroundColor: "#ffffff", width: 500, height: "auto", borderRadius: 4, marginTop: 5, display: "flex", flexDirection: "column", padding: 5, }}>
                { error && <Alert severity="error">{error}</Alert> }
                <TextField label="Email" margin="dense" type="email" onChange={(e) => {
                    setEmail(e.target.value);
                }}/>

                <TextField label="Password" margin="dense" type="password" onChange={(e) => {
                    setPassword(e.target.value);
                }}/>

                <Button onClick={login} variant="contained" sx={{ marginTop: 2, }}>Login</Button>

                <div>
                    Don't have an account? <Link to="/signup" style={{ color: '#000' }}>Create one here</Link>
                </div>
                <div>
                    <Link to="/request-password-reset" style={{ color: '#000' }}>Forgot your password?</Link>
                </div>
            </Container>
        </>
    )
}

export default Login;

firebase.js

import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';

const firebaseConfig = {
    apiKey,
    authDomain,
    projectId,
    storageBucket,
    messagingSenderId,
    appId,
    measurementId
}

const app = initializeApp(firebaseConfig);
const auth = getAuth(app);

export {
    auth
};

App.js

import './App.css';
import Home from './components/Pages/Home';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Signup from './components/Pages/Signup/Signup';
import Login from './components/Pages/Login/Login';
import UserDashboard from './components/Pages/UserDashboard/UserDashboard';
import ForgotPassword from './components/Pages/Forgot-Password';

function App() {
  return (
      <Router>
          <div>
            <Switch>
              <Route exact path="/" component={Home}/>
              <Route path="/signup" component={Signup}/>
              <Route path="/login" component={Login}/>
              <Route path="/dashboard" component={UserDashboard}/>
              <Route path="/request-password-reset" component={ForgotPassword}/>
            </Switch>
          </div>
      </Router>
  );
}

export default App;

如果您尝试创建私有路由组件而不在您的应用程序中的某处保留身份验证状态并通过 React 上下文公开,那么您将需要在每次路由更改时异步检查身份验证状态。这意味着在进行身份验证状态检查时,您还需要“正在加载”或“待定”状态。

这是一个没有任何持久状态的自定义专用路由的示例实现。

import { useEffect, useState } from 'react';
import { Route, Redirect } from 'react-router-dom'; // v4/5
import { onAuthStateChanged } from 'firebase/auth';
import { auth }from '../../../firebase';

const PrivateRoute = props => {
  const [pending, setPending] = useState(true);
  const [currentUser, setCurrentUser] = useState();

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(
      auth,
      user => {
        setCurrentUser(user);
        setPending(false);
      },
      error => {
        // any error logging, etc...
        setPending(false);
      }
    );

    return unsubscribe; // <-- clean up subscription
  }, []);

  if (pending) return null; // don't do anything yet

  return currentUser 
    ? <Route {...props} />      // <-- render route and component
    : <Redirect to="/login" />; // <-- redirect to log in
};