React 路由器专用路由没有从状态(例如身份验证状态)获取 props



我正在尝试实现一个专用路由组件来重定向未经身份验证的用户。问题是当我像<PrivateRoute authenticated={this.state.isAuthenticated} path='/private' component={Panel} currentUser={this.state.currentUser}一样渲染组件时,私有路由会将经过身份验证的用户重定向到登录页面,而不是转到面板。

App.js中,我渲染了包括<PrivateRoute/>在内的所有路由,并在ComponentDidMount()中设置了currentUserisAuthenticated状态变量,但我无法将它们传递给PrivateRoute

应用.js

//imports...
class App extends Component {
state = {
currentUser: null,
isAuthenticated: false,
isLoading: false
}
loadCurrentUser = () => {
this.setState({
isLoading: true
});
// imported method
getCurrentUser()
.then(response => {
this.setState({
currentUser: response,
isAuthenticated: true,
isLoading: false
});
})
.catch(error => {
console.log(error)
this.setState({
isLoading: false
});  
});
}
componentDidMount() {
this.loadCurrentUser();
}
handleLogin = () => {
this.loadCurrentUser();
this.props.history.push("/");
}
render () {
return (
<React.Fragment>
<Navigation
currentUser={this.state.currentUser}
isAuthenticated={this.state.isAuthenticated}
handleLogout={this.handleLogout} />
<Switch>
<PrivateRoute
authenticated={this.state.isAuthenticated}
exact
path='/postulante'
component={Panel}
currentUser={this.state.currentUser} />
<Route
exact
path='/'
render={
(props) => <Landing {...props} />
} />
<Route
path="/login"
exact
render={
(props) =>  <Login onLogin={this.handleLogin} {...props} />
} />
</Switch>
</React.Fragment>
);
}
}
export default withRouter(App);

请注意,<Navigation />组件确实使状态变量正确。

私人路线.js

//imports...
const PrivateRoute = ({ component: Component, authenticated, ...rest }) => (
<Route
{...rest}
render={props =>
authenticated ? (
<Component {...rest} {...props} />
) : (
<Redirect
to={{
pathname: '/login',
state: { from: props.location }
}}
/>
)
}
/>
);
export default PrivateRoute

问题与第一次渲染的PrivateRoute组件有关,而主 App 组件没有任何更新的道具。

如果您直接导航到PrivateRoute路径而不先进入任何其他路由,您将被重定向回/login。您的PrivateRoute会尝试在父应用的componentDidMount()逻辑完成之前呈现。所以 isAuthenticated 被传递为假。

相反的情况,如果你从比方说HomeLogin开始,你使用LinkPrivateRoute

最终,这就是为什么人们使用像 redux 这样的状态管理工具来全局共享经过身份验证的状态,而不是通过父组件传递。

虽然有一个解决方法!

请参阅沙盒以供参考:https://codesandbox.io/s/intelligent-dan-uskcy

  1. 我们可以通过使用额外的状态值来检查 应用程序组件是否曾经初始化过。我们称之为wasInitialized
  2. PrivateRoute 将接收它作为称为 wasInitialized的 prop,如果 我们直接进入它的组件路径,wasInitialized在 App 有机会完成其componentDidMount()逻辑之前,该路径将为 false。
  3. 如果 wasInitialized 是假的,我们不会重定向到/login, 相反,我们将只显示一个空字符串,给父应用程序的componentDidMount()执行和更新isAuthenticated价值。
  4. 所以现在让我们看一下这一行:

    <Route {...rest} render={props => auth === true ? <Component {...props} /> : !wasInitialized ? "" : <Redirect to="/login" /> }

    在下一次重新渲染中,isAuthenticated将为 true 或 假。如果用户经过身份验证,我们将呈现预期的组件。如果用户未通过身份验证,我们将进行下一次检查。现在 wasInitialized 的值为 true,因此 check 的计算结果为 false。因此,由于两项检查都没有通过,因此我们重定向到/login.

应用.js

class App extends Component {
state = {
currentUser: null,
isAuthenticated: false,
isLoading: false,
wasInitialized: false
}
loadCurrentUser = () => {
this.setState({
isLoading: true
});
// imported method
getCurrentUser()
.then(response => {
this.setState({
currentUser: response,
isAuthenticated: true,
wasInitialized: true,
isLoading: false
});
})
.catch(error => {
console.log(error)
this.setState({
isLoading: false,
wasInitialized: true
});  
});
}
componentDidMount() {
this.loadCurrentUser();
}
handleLogin = () => {
this.loadCurrentUser();
this.props.history.push("/");
}
render () {
return (
<React.Fragment>
<Navigation
currentUser={this.state.currentUser}
isAuthenticated={this.state.isAuthenticated}
handleLogout={this.handleLogout} />
<Switch>
<PrivateRoute
authenticated={this.state.isAuthenticated}
path='/postulante'
component={Panel}
wasInitialized={this.state.wasInitialized}
currentUser={this.state.currentUser} />
<Route
exact
path='/'
render={
(props) => <Landing {...props} />
} />
<Route
path="/login"
exact
render={
(props) =>  <Login onLogin={this.handleLogin} {...props} />
} />
</Switch>
</React.Fragment>
);
}
}
export default withRouter(App);

私人

import React from "react";
import { Route, Redirect } from "react-router-dom";
const PrivateRoute = ({
component: Component,
auth,
wasInitialized,
...rest
}) => {
return (
<Route
{...rest}
render={props =>
auth === true ? (
<Component {...props} />
) : !wasInitialized ? (
""
) : (
<Redirect to="/login" />
)
}
/>
);
};
export default PrivateRoute;

最新更新