使用 react-router-dom 重新加载页面时出现问题
Posted
技术标签:
【中文标题】使用 react-router-dom 重新加载页面时出现问题【英文标题】:Problem when reloading page using react-router-dom 【发布时间】:2021-08-16 08:08:20 【问题描述】:我设法创建了一条私人路线并使用react-router-dom
导航到不同的页面。但是,当我导航到一个页面并重新加载它时,它首先会转到 /login
半秒钟,然后正确地重新加载页面。如何防止这种不良行为并改进我的路由?
这是我的路线:
<Router>
<Route
path="/"
component=() =>
!auth ? <Redirect to="/login" /> : <Redirect to=path />
/>
<Route exact path="/home" component=Home />
<Route exact path="/dashboard" component=Dashboard />
<Route exact path="/login" component=RedirectPage />
</Router>
这是完整的组件:
import
Route,
BrowserRouter as Router,
Link,
Redirect,
from "react-router-dom";
import Container, Button from "@material-ui/core/";
import Login from "./Login";
import useContext,useState from "react";
import UserContext from "../App";
import signOut from "../Storage/Auth";
const Routes = () =>
const auth, setAuth, logging = useContext(UserContext);
const [path,setPath] = useState("/home")
const handleSignOut = () =>
signOut(setAuth);
console.log("Auth", auth);
;
const Home = () =>
console.log("Home");
return (
<Container>
<h1>Welcome</h1>
<Link to="/">
<Button onClick=handleSignOut> Log Out</Button>
</Link>
<Link to="/dashboard">
<Button> Dash</Button>
</Link>
</Container>
);
;
const Dashboard = () =>
setPath("/dashboard")
console.log("Dash");
return (
<Container>
<Link to="/home">
<Button> HOME</Button>
</Link>
<h1>Dashboard</h1>
</Container>
);
;
const RedirectPage = () =>
if (!logging)
return <div></div>;
else
return <Login />;
;
return (
<Router>
<Route
path="/"
component=() =>
!auth ? <Redirect to="/login" /> : <Redirect to=path />
/>
<Route exact path="/home" component=Home />
<Route exact path="/dashboard" component=Dashboard />
<Route exact path="/login" component=RedirectPage />
</Router>
);
;
export Routes ;
这是我的Login
组件。
import useState, useContext from "react";
import
Button,
Card,
Container,
Typography,
Box,
TextField,
from "@material-ui/core/";
import useHistory from "react-router-dom";
import signIn from "../Storage/Auth";
import UserContext from "../App";
const Login = () =>
const [mail, setMail] = useState<string>("");
const [password, setPassword] = useState<string>("");
const user, setUser = useContext(UserContext);
const handleSignIn = async (m: string, p: string) =>
await signIn(m, p).then((e) =>
console.log("USERID", e, user);
setUser(e);
);
;
const history = useHistory();
const handleEnter = () =>
history.push("/home");
;
const handleOnKey = (e: any) =>
if (e.key === "Enter")
e.preventDefault();
handleSignIn(mail, password);
handleEnter();
;
return (
<Card className="Card" raised=true>
<Container className="Input">
<Typography className="Sign-in" paragraph=true variant="inherit">
Sign in
</Typography>
<Box
className="Box"
borderColor="error.main"
border=2
borderRadius="borderRadius"
>
<Container>
<TextField
fullWidth=true
placeholder=" email"
value=mail
onChange=(e) =>
setMail(e.target.value);
onKeyDown=(e) =>
handleOnKey(e);
/>
</Container>
</Box>
</Container>
<Container className="Input">
<Box
className="Box"
borderColor="error.main"
borderRadius="borderRadius"
border=2
>
<Container>
<TextField
fullWidth=true
placeholder=" password"
value=password
onChange=(e) =>
setPassword(e.target.value);
type="password"
onKeyDown=(e) =>
handleOnKey(e);
/>
</Container>
</Box>
<h1> </h1>
<Button
onClick=() =>
handleSignIn(mail, password);
fullWidth=true
color="primary"
variant="contained"
type="submit"
>
Sign In" "
</Button>
<h1> </h1>
<Box className="Sign-in">
<Button size="small"> Register </Button>
</Box>
<h1> </h1>
</Container>
</Card>
);
;
export default Login;
这是App
组件:
import useEffect from "react";
import Routes from "./Routing/Routes";
import "./App.css";
import Container from "@material-ui/core/";
import initFirebase from "./Storage/Secret";
import useState, createContext from "react";
import onAuthChange from "./Storage/Auth";
export const UserContext = createContext<any>(null);
function App()
const [user, setUser] = useState(null);
const [auth, setAuth] = useState<string | null>("");
const [logging, setLogging] = useState(null)
useEffect(() =>
initFirebase();
, []);
useEffect(() =>
onAuthChange(setAuth,setLogging);
, [auth]);
return (
<UserContext.Provider value= user, setUser, auth,setAuth,logging >
<div className="App">
<Container>
<Routes />
</Container>
</div>
</UserContext.Provider>
);
export default App;
另外,这里是auth
逻辑:
import firebase from "firebase/app";
import "firebase/auth";
const auth = () => firebase.auth();
const signIn = async (email, password) =>
await auth()
.signInWithEmailAndPassword(email, password)
.then((userCredential) =>
var user = userCredential.user;
console.log("USER", user);
return user.uid;
)
.catch((error) =>
var errorCode = error.code;
var errorMessage = error.message;
alert(errorCode, errorMessage);
return null;
);
;
const onAuthChange = (setState, setLoading) =>
auth().onAuthStateChanged((u) =>
if (!u)
console.log(u);
setLoading(true);
else
setState(u);
setLoading(false);
);
;
const signOut = (setState) =>
auth()
.signOut()
.then(function ()
console.log("LOGGED OUT");
)
.catch(function (error)
console.log("ERROR LOGGING OUT");
);
setState(null);
;
export signIn, signOut, onAuthChange
最后,完整代码在https://gitlab.com/programandoconro/adminkanjicreator
任何建议将不胜感激,谢谢。
【问题讨论】:
您能否分享您的私有路由组件,以便我们在解析身份验证状态时查看它的作用,以及您使用私有路由的代码?我怀疑你没有实际上有一个PrivateRoute
组件虽然通过你的代码sn-p 来判断。您还可以包括您的UserContext
代码吗?如果您在实施身份验证流程方面需要帮助,请查看docs 以获取身份验证工作流程示例。
@DrewReese 我更新了问题,并将此链接添加到完整的 repo gitlab.com/programandoconro/adminkanjicreator
auth
状态的正常值是多少?您可以从与“已验证”或“未验证”状态不同的初始状态开始,然后等待 not 的值等于初始状态。
@DrewReese 我添加了身份验证逻辑。谢谢您的建议。我会对此进行试验。
【参考方案1】:
我建议早点进行身份验证检查。所以像这样的东西,这样路由本身只有在 auth 中有东西时才会被渲染。我认为您的示例也缺少通常有帮助的 Switch 语句。
<Router>
!auth ? (
<Switch>
<Route exact path="/login" component=RedirectPage />
</Switch>
) : (
<Switch>
<Route exact path="/home" component=Home />
<Route exact path="/dashboard" component=Dashboard />
</Switch>
)
</Router>
【讨论】:
我正在试验您的建议以使其发挥作用。谢谢。【参考方案2】:通常,您会希望某种“加载”或“不确定”状态表示既不经过身份验证也不表示未经身份验证。您可以使用这第三个“状态”来保存 UI,然后根据身份验证在任何事物上呈现一种或另一种方式。
由于您的 auth
逻辑解析为布尔值 true
|false
。
const onAuthChange = (setState, setLoading) =>
auth().onAuthStateChanged((u) =>
if (!u)
console.log(u);
setLoading(true);
else
setState(u);
setLoading(false);
);
;
您可以使用初始auth
状态不是这两者的事实。我建议使用null
。
const [auth, setAuth] = useState<string | null>(null);
当使用auth
状态渲染Route
时,您可以增加逻辑以在决定重定向之前提前返回。
<Route
path="/"
render=() =>
if (auth === null) return null;
return <Redirect to=auth ? path : "/login" />;
/>
请注意,我也切换到了 render
属性,component
属性用于附加实际的 React 组件。这些处理方式略有不同。您可以阅读有关路由渲染方法的差异here。
完整的路由器示例:
<Router>
<Switch>
<Route path="/home" component=Home />
<Route path="/dashboard" component=Dashboard />
<Route path="/login" component=RedirectPage />
<Route
path="/"
render=() =>
if (auth === null) return null;
return <Redirect to=auth ? path : "/login" />;
/>
</Switch>
</Router>
请注意,我还包含了Switch
组件并重新排序了路由,因此在不太具体的路径之前列出了更具体的路径。这允许您从所有路由中删除不必要的 exact
属性,因为 Switch
仅呈现路由(相对于 Router
所做的包含性)。
【讨论】:
【参考方案3】:我终于设法解决了这个问题。现在重新加载工作完美,安全性作为例外实施。这是我最后的Router
:
<Router>
<Route
path="/"
render=() =>
logging ? <Redirect to="/login" /> : <Redirect to=path />
/>
<Route exact path="/" render=() => auth && <Home /> />
<Route exact path="/dashboard" render=() => auth && <Dashboard /> />
<Route exact path="/login" component=Login />
</Router>
这是组件现在的样子。
import
Route,
BrowserRouter as Router,
Link,
Redirect
from "react-router-dom";
import Container, Button from "@material-ui/core/";
import Login from "./Login";
import useContext, useState, useEffect from "react";
import UserContext from "../App";
import signOut from "../Storage/Auth";
const Routes = () =>
const auth, setAuth, logging = useContext(UserContext);
const handleSignOut = () =>
signOut(setAuth);
console.log("Auth", auth);
;
const pathname = window.location.pathname;
const [path, setPath] = useState(pathname);
useEffect(() =>
console.log(path);
path === "/login" && setPath("/");
path !== "/" && path !== "/dashboard" && setPath("/");
, [auth]);
const Home = () =>
console.log("Home");
return (
<Container>
<h1>Welcome</h1>
<Link to="/">
<Button onClick=handleSignOut> Log Out</Button>
</Link>
<Link to="/dashboard">
<Button> Dash</Button>
</Link>
</Container>
);
;
const Dashboard = () =>
console.log("Dash");
return (
<Container>
<Link to="/">
<Button> HOME</Button>
</Link>
<h1>Dashboard</h1>
</Container>
);
;
return (
<Router>
<Route
path="/"
render=() =>
logging ? <Redirect to="/login" /> : <Redirect to=path />
/>
<Route exact path="/" render=() => auth && <Home /> />
<Route exact path="/dashboard" render=() => auth && <Dashboard /> />
<Route exact path="/login" component=Login />
</Router>
);
;
export Routes ;
感谢@Richard 和@Drew 的支持。
【讨论】:
以上是关于使用 react-router-dom 重新加载页面时出现问题的主要内容,如果未能解决你的问题,请参考以下文章
使用 ajax 分页加载页面后重新初始化其他 javascript 函数
使用 react-router-dom 停止页面中侧边栏的重新渲染
带有 react-router-dom NavLinks 的 react-bootstrap 导航栏中的 collapseOnSelect