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);
}
}
我目前在通过身份验证时遇到一些路由问题。每当我尝试在 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);
}
}