Проблема связана с компонентом PrivateRoute
, который рендерится впервые без каких-либо обновленных реквизитов из основного компонента приложения.
Если вы должны были перейти непосредственно к пути PrivateRoute
, не входя сначала в какой-либо другой маршрут, вы будете перенаправлены обратно на /login
. Ваш PrivateRoute
пытается выполнить рендеринг до завершения логики componentDidMount()
родительского приложения. Поэтому isAuthenticated передается как ложный
Обратное произойдет, если вы начнете с, скажем, Home
или Login
, и вы используете Link
, чтобы перейти к PrivateRoute
.
В конечном счете, именно поэтому люди используют инструмент управления состоянием, такой как, например, redux, для предоставления общего доступа к аутентифицированному состоянию вместо его передачи через родительские компоненты.
Хотя есть обходной путь!
См. Песочницу для справки: https://codesandbox.io/s/intelligent-dan-uskcy
- Мы можем обойти это, используя дополнительное значение состояния для проверки
был ли компонент приложения когда-либо инициализирован. Мы назовем это
wasInitialized
- PrivateRoute получит это как prop под названием wasInitialized, и если
мы идем прямо к его компоненту-пути,
wasInitialized
будет ложным, пока приложение не сможет завершить свою componentDidMount()
логику.
- Если wasInitialized - false, мы не будем перенаправлять на / login,
вместо этого мы просто отобразим пустую строку, давая родительскому приложению
componentDidMount()
время выполнения и обновления
isAuthenticated
значение.
Итак, теперь давайте посмотрим на эту строку:
<Route {...rest} render={props => auth === true ? <Component
{...props} /> : !wasInitialized ? "" : <Redirect to="/login" />
}
В следующем повторном рендере isAuthenticated
будет либо истинным, либо
ложный. Если пользователь isAuthenticated, мы визуализируем ожидаемый компонент. Если пользователь не аутентифицирован, мы переходим к следующей проверке. Теперь wasInitialized будет иметь значение true, так что проверка оценивается как false. Таким образом, поскольку обе проверки не проходят, мы перенаправляем на /login
.
App.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);
1052 * Частный *
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;