React-Router-Dom 无法呈现页面但由于 PrivateRoute 路由返回

React-Router-Dom unable to render page but routes back due to PrivateRoute

我目前在通过身份验证时遇到一些路由问题。每当我尝试在 localhost:3000/portfolio/portfolioId 访问我的 ViewPortfolio 页面时,它都会将我重定向回我的主页。我不确定发生了什么。我还尝试通过将 URL 修改为正确的 URL link 来操纵 URL,但当我通过身份验证时,它也会将我重定向回 /homepage。源代码可以在下面找到。 App.js 是我的路由器,使用 PrivateRoute 作为私有路由组件,最后,CreateInterview.js 我使用 js windows.location 函数重定向到 ViewPortfolio.js,它将使用 useParams() react hook获取参数。但是现在,在成功创建并使用 portfolioId 重定向到正确的 URL 后,它将在不到一秒内重定向回主页。

PrivateRoute.js

import React from 'react' 
import { Route, Redirect } from 'react-router-dom' 
import { useAuth } from '../contexts/AuthContext' 
 
const PrivateRoute = ({ component: Component, ...rest }) => { 
  const { currentUser } = useAuth() 
 
  return ( 
    <Route 
      {...rest} 
      render={(props) => { 
        if (currentUser) { 
          return <Component {...props} /> 
        } else { 
          return <Redirect to={{ 
            pathname: "/", 
            state:{ 
              from: props.location 
            } 
          }}/> 
        } 
      } 
      }> 
 
    </Route> 
  ) 
} 
 
export default PrivateRoute

App.js

import React from "react" 
.
.
.
import PublicRoute from "./PublicRoute"; 
 
function App() { 
    return ( 
        <AuthProvider> 
            <Router> 
                <Switch> 
                    {/* Auth Routes */} 
                    <PublicRoute exact path='/' component={Login} /> 
                    .
                    .
                    .
                    <PrivateRoute exact path='/createInterview' component={CreateInterview} /> 
                    <PrivateRoute path='/manageInterview' component={ManageInterview} /> 
                    <PrivateRoute path='/portfolio/:portfolioId' component={ViewPortfolio} /> 
 
                    {/* Non-Existance Routes */} 
                    <Route path="*" component={() => "404 NOT FOUND"} /> 
                </Switch> 
            </Router> 
        </AuthProvider> 
    ) 
} 
 
export default App

CreatInterview.js 在 js 中重定向(表单的 onSubmit)

async function handleSubmit(e) { 
    e.preventDefault(); 
    setError(''); 
    setLoading(true); 
 
    await database.portfolioRef.add({ 
      intervieweeName: intervieweeNameRef.current.value, 
      intervieweeEmail: intervieweeEmailRef.current.value, 
      intervieweeMobileNumber: intervieweeMobileRef.current.value, 
      projectTitle: projectTitleRef.current.value, 
      portfolioTitle: portfolioNameRef.current.value, 
      dateCreated: new Date().toLocaleString('en-SG'), 
      createdBy: currentUser.displayName 
    }).then(function(docRef) { 
      console.log("This is the Document ID " + docRef.id.toString()); 
      console.log(docRef.id); 
      window.location = '/portfolio/' + docRef.id; 
    }) 
 
    setLoading(false) 
  }

ViewPortfolio.js 的一部分从 CreateInterview.js

接收 portfolioId
const ViewPortfolio = () => { 
    let { portfolioId } = useParams(); 

AuthContext.js

import React, { useContext, useState, useEffect } from "react" 
import { auth, database } from "../firebase"; 
import { getDocs, query, where } from "firebase/firestore"; 
 
const AuthContext = React.createContext() 
 
export function useAuth() { 
    return useContext(AuthContext) 
} 
 
export function AuthProvider({ children }) { 
    const [currentUser, setCurrentUser] = useState(null) 
    const [loading, setLoading] = useState(true) 
 
    function login(email, password) { 
        return auth.signInWithEmailAndPassword(email, password).then(() => { 
            const Doc = query(database.usersRef, where("email", "==", email)); 
 
            getDocs(Doc).then((querySnapshot) => { 
                let values = ''; 
 
                querySnapshot.forEach((doc) => { 
                    values = doc.id; 
                }); 
 
                var userUpdate = database.usersRef.doc(values); 
                userUpdate.update({ 
                    lastActive: new Date().toLocaleString('en-SG'), 
                }) 
            }) 
        }); 
    } 
 
    function logout() { 
        return auth.signOut(); 
    } 
 
    function forgetPassword(email) { 
        return auth.sendPasswordResetEmail(email); 
    } 
 
    function updateEmail(email) { 
        return currentUser.updateEmail(email) 
    } 
 
    function updatePassword(password) { 
        return currentUser.updatePassword(password) 
    } 
 
    function updateDisplayName(name) { 
        return currentUser.updateDisplayName(name) 
    } 
 
    useEffect(() => { 
        const unsubscribe = auth.onAuthStateChanged( user => { 
            setLoading(false) 
            setCurrentUser(user) 
        }) 
 
        return unsubscribe 
    }, []) 
 
    const value = { 
        currentUser, 
        login, 
        forgetPassword, 
        logout, 
        updateEmail, 
        updatePassword, 
        updateDisplayName, 
    } 
 
    return ( 
        <AuthContext.Provider value={value}> 
            {!loading && children} 
        </AuthContext.Provider> 
    ) 
}

初始 currentUser 状态与未验证状态匹配,因此当应用程序最初呈现时,如果您正在访问受保护的路由,则会发生重定向,因为 currentUser 状态尚未更新。

因为 onAuthStateChanged returns null 对于未经身份验证的用户,我建议使用 除了 null 之外的任何东西作为初始 currentUser状态。 undefined 是一个很好的不确定值。您可以使用此不确定值有条件地呈现加载指示器,或根本不呈现任何内容,同时在初始呈现时确认身份验证状态。

授权提供者

export function AuthProvider({ children }) { 
  const [currentUser, setCurrentUser] = useState(); // <-- undefined
  ...

私有路由

const PrivateRoute = (props) => { 
  const { currentUser } = useAuth();

  if (currentUser === undefined) {
    return null; // or loading spinner, etc...
  }

  return currentUser
    ? (
      <Route {...props} />
    )
    : (
      <Redirect
        to={{ 
          pathname: "/", 
          state: { 
            from: props.location 
          } 
        }}
      />
    );
}

您还应该真正用 history.push('/portfolio/' + docRef.id); 替换 window.location = '/portfolio/' + docRef.id; 逻辑,这样您就不会不必要地重新加载页面。

const history = useHistory();

...

async function handleSubmit(e) { 
  e.preventDefault(); 
  setError(''); 
  setLoading(true); 

  try {
    const docRef = await database.portfolioRef.add({ 
      intervieweeName: intervieweeNameRef.current.value, 
      intervieweeEmail: intervieweeEmailRef.current.value, 
      intervieweeMobileNumber: intervieweeMobileRef.current.value, 
      projectTitle: projectTitleRef.current.value, 
      portfolioTitle: portfolioNameRef.current.value, 
      dateCreated: new Date().toLocaleString('en-SG'), 
      createdBy: currentUser.displayName 
    });
     
    history.push('/portfolio/' + docRef.id);
  } catch (error) {
    // handle error, clear loading state
    setLoading(false);
  }
}