React Router v6调用history.push仅URL变更未跳转页面问题排查
问题核心原因
- 你自行创建的
customHistory和react-router v6中BrowserRouter内部维护的history实例不是同一个,你调用自定义history的push方法只能修改地址栏URL,无法触发Router内部的路由状态更新,所以页面不会重新渲染。 - Login组件的
render函数中直接执行localStorage.removeItem('accessToken'),每次组件渲染都会清空token:你登录验证通过后异步存储token的操作完成后,组件会触发重渲染,刚存储的token会被立即清空,导致PrivateRoute鉴权失败,自然无法跳转到私有路由页面。
解决方案
步骤1:删除自定义history相关代码
修改routes.js,移除自定义history的创建和传参,同时实现适配类组件的withRouter高阶组件(v6官方移除了withRouter,可自行实现):
import React from 'react'; import { BrowserRouter as Router, Routes, Route, useNavigate } from 'react-router-dom' import HomePage from './App/pages/HomePage' import UsersPage from './App/pages/UsersPage' import Login from './App/pages/Login' import PrivateRoute from './Auth' // 适配类组件的withRouter高阶组件 function withRouter(Component) { function ComponentWithRouterProp(props) { const navigate = useNavigate(); return <Component {...props} navigate={navigate} />; } return ComponentWithRouterProp; } function RoutesController() { return ( <Router> <Routes> <Route path="/" element={<Login />} /> <Route path="/home" element={<PrivateRoute />}> <Route path="" element={<HomePage />} /> </Route> <Route path="/users" element={<PrivateRoute />}> <Route path="" element={<UsersPage />} /> </Route> <Route path="*" element={<h1>NOT FOUND</h1>} /> </Routes> </Router> ) } export default RoutesController
步骤2:修改Login组件适配v6导航逻辑
- 导出Login时用上述
withRouter包裹,获取navigate方法 - 将render中的token清空逻辑移到
componentDidMount生命周期,避免每次渲染都删除token - 登录成功后调用
navigate方法跳转
修改后的Login.jsx核心代码:
import React, { Component } from "react"; import Wrapper from "../../../components/Wrapper"; import "./style.css" import logo from "../../../assets/icons/logotipo_semfundo.png" import Button from "@material-ui/core/Button"; import { MdEmail, MdLock } from "react-icons/md" // 导入刚才实现的withRouter,或者单独抽离成公共文件引入 import { withRouter } from './routes' class Login extends Component { constructor(props) { super(props) this.state = { message: '', } } componentDidMount() { // 仅在组件首次挂载时清空token,不要放在render中 localStorage.removeItem('accessToken') } signIn = () => { const data = { email: this.email, password: this.password } const requestInfo = { method: 'POST', body: JSON.stringify(data), headers: new Headers({ 'Content-Type': 'application/json' }), } fetch('http://localhost:3000/api/login', requestInfo).then(response => { if (response.ok) { return response.json() } throw new Error('Login Inválido...') }).then(token => { localStorage.setItem('accessToken', token) // 用v6的navigate方法跳转 this.props.navigate("/home", {state: {message: "Ok"}}) return } ).catch(e => (this.setState({ message: e.message }))) } render() { return ( <Wrapper className="login"> <Wrapper className="login-img"> <img src={logo} alt="logo" /> </Wrapper> <Wrapper className="login-form"> <h1 className="h1" >Login</h1> { this.state.message !== '' ? ( <span color="black">{this.state.message}</span> ) : '' } <form id="loginForm"> <Wrapper> <Wrapper> <MdEmail /> <input id="email" type="text" name="email" placeholder="Digite um E-mail" onChange={e => this.email = e.target.value} ></input> </Wrapper> <Wrapper> <MdLock /> <input id="password" type="password" name="password" placeholder="Senha" onChange={e => this.password = e.target.value} ></input> </Wrapper> <Wrapper> <Button onClick={() => this.signIn()} color="primary" > Entrar </Button> </Wrapper> </Wrapper> </form> </Wrapper> </Wrapper> ) } } // 用withRouter包裹组件,注入navigate方法 export default withRouter(Login)
内容的提问来源于stack exchange,提问作者Luca Oliveira
相关产品推荐
相关产品推荐

