React Router PrivateRoute路由无限循环问题求解
问题分析与修复方案
嘿,我帮你定位到几个关键问题了,咱们一个个解决:
1. PrivateRoute的布尔值误用(最核心问题)
在你的Main.jsx里,PrivateRoute的判断逻辑写了this.state.authed()——但authed是你存在state里的布尔值,不是函数!这会导致每次判断都返回undefined(布尔值无法被调用),所以PrivateRoute永远认为用户未认证,直接重定向到/login,这就是无限循环的根源。
修复方法:把this.state.authed()改成this.state.authed:
// Main.jsx里的PrivateRoute修正 const PrivateRoute = ({component: Component, ...rest}) => ( <Route {...rest} render={props => this.state.authed ? (<Component {...props} />) : (<Redirect to="/login"/>) } /> );
2. Login组件的handleLogin调用时机错误
你在componentWillUnmount里调用this.props.handleLogin,但这个钩子是组件卸载时才触发的。当Login组件因为redirect卸载时,Main组件的state还没来得及更新,PrivateRoute依然用旧的authed状态判断,继续重定向回Login,形成循环。
修复方法:在登录成功拿到token后,立即调用handleLogin,而不是等组件卸载:
// Login.jsx里的axios回调修正 axios.post('/api/login', { 'client_id': '3', 'email': this.state.email, 'password': this.state.password, 'confirm_password': this.state.password }).then((response) => { let token = response.data.access_token; // 先调用父组件的handleLogin更新认证状态 this.props.handleLogin(token); // 再设置redirect this.setState({ redirect: true, token: token, loading: false }); }, (error) => { console.error('error', error.response.data); this.setState({ error: error.response.data, loading: false }); });
同时删掉componentWillUnmount里的this.props.handleLogin(this.state.token),这个已经不需要了。
3. 额外优化:Main组件的认证状态同步
可以在Main.jsx里添加componentDidMount钩子,再次检查localStorage,避免初始状态因为某些原因不准确:
// Main.jsx里添加componentDidMount componentDidMount() { this.setState({ authed: localStorage.getItem('access_token') !== null }); }
修复后的完整关键代码片段
Main.jsx(核心修正部分)
class Main extends Component { constructor(props) { super(props); this.handleLogout = this.handleLogout.bind(this); this.handleLogin = this.handleLogin.bind(this); this.state = {authed: localStorage.getItem('access_token') !== null}; } // 新增:挂载时同步localStorage状态 componentDidMount() { this.setState({ authed: localStorage.getItem('access_token') !== null }); } handleLogout(event) { event.preventDefault(); localStorage.removeItem('access_token'); this.setState({authed: false}); } handleLogin(token) { localStorage.setItem('access_token', token); this.setState({authed: token !== null}); } render() { // 修正authed的调用方式 const PrivateRoute = ({component: Component, ...rest}) => ( <Route {...rest} render={props => this.state.authed ? (<Component {...props} />) : (<Redirect to="/login"/>) } /> ); const LoginLogout = () => { return this.state.authed ? (<button onClick={this.handleLogout}>Logout</button>) : (<Link to="/login">Login</Link>); }; return ( <BrowserRouter> <div> <ul> <li> <Link to="/">Home</Link> </li> <li> <LoginLogout/> </li> </ul> <Route path="/login" component={() => <Login handleLogin={this.handleLogin}/>}/> <PrivateRoute exact path="/" component={Home}/> </div> </BrowserRouter> ); } }
Login.jsx(核心修正部分)
export class Login extends Component { constructor(props) { super(props); this.state = { email: '', password: '', redirect: localStorage.getItem('access_token') !== null, token: null, loading: false, error: null }; this.handleInputChange = this.handleInputChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } // 删掉这个生命周期钩子 // componentWillUnmount() { // this.props.handleLogin(this.state.token); // } handleInputChange(event) { const target = event.target; const value = target.type === 'checkbox' ? target.checked : target.value; const name = target.name; this.setState({ [name]: value }); } handleSubmit(event) { event.preventDefault(); this.setState({ error: null, loading: true }); axios.post('/api/login', { 'client_id': '3', 'email': this.state.email, 'password': this.state.password, 'confirm_password': this.state.password }).then((response) => { let token = response.data.access_token; // 先更新父组件状态 this.props.handleLogin(token); this.setState({ redirect: true, token: token, loading: false }); }, (error) => { console.error('error', error.response.data); this.setState({ error: error.response.data, loading: false }); }); } render() { if (this.state.redirect) return (<Redirect to={"/"}/>); return ( <form onSubmit={this.handleSubmit}> <label htmlFor="email">邮箱</label> <input type="email" name="email" value={this.state.email} onChange={this.handleInputChange} /> <label htmlFor="password">密码</label> <input type="password" name="password" value={this.state.password} onChange={this.handleInputChange} /> <button type="submit" disabled={this.state.loading}>登录</button> {this.state.error && <p>{this.state.error}</p>} </form> ); } }
这样修改后,PrivateRoute就能正确判断用户的认证状态,Login成功后也能及时更新Main组件的state,避免无限循环问题啦!
内容的提问来源于stack exchange,提问作者CrunchyArtie
相关产品推荐
相关产品推荐

