Реагировать на частный маршрут маршрутизатора, не получая реквизиты из состояния (например, состояние аутентификации) - PullRequest
0 голосов
/ 22 июня 2019

Я пытаюсь реализовать частный компонент маршрута для перенаправления пользователей, которые не прошли проверку подлинности. Проблема в том, что когда я рендерил компонент как <PrivateRoute authenticated={this.state.isAuthenticated} path='/private' component={Panel} currentUser={this.state.currentUser}, частный маршрут перенаправляет прошедших проверку пользователей на страницу входа в систему, а не на панель.

В App.js я отображаю все маршруты, включая <PrivateRoute/>, и я устанавливаю переменные состояния currentUser и isAuthenticated в ComponentDidMount(), но я не могу передать их в PrivateRoute.

App.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 /> правильно определяет переменные состояния.

PrivateRoute.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

1 Ответ

1 голос
/ 22 июня 2019

Проблема связана с компонентом PrivateRoute, который рендерится впервые без каких-либо обновленных реквизитов из основного компонента приложения.

Если вы должны были перейти непосредственно к пути PrivateRoute, не входя сначала в какой-либо другой маршрут, вы будете перенаправлены обратно на /login. Ваш PrivateRoute пытается выполнить рендеринг до завершения логики componentDidMount() родительского приложения. Поэтому isAuthenticated передается как ложный

Обратное произойдет, если вы начнете с, скажем, Home или Login, и вы используете Link, чтобы перейти к PrivateRoute.

В конечном счете, именно поэтому люди используют инструмент управления состоянием, такой как, например, redux, для предоставления общего доступа к аутентифицированному состоянию вместо его передачи через родительские компоненты.

Хотя есть обходной путь!

См. Песочницу для справки: https://codesandbox.io/s/intelligent-dan-uskcy

  1. Мы можем обойти это, используя дополнительное значение состояния для проверки был ли компонент приложения когда-либо инициализирован. Мы назовем это wasInitialized
  2. PrivateRoute получит это как prop под названием wasInitialized, и если мы идем прямо к его компоненту-пути, wasInitialized будет ложным, пока приложение не сможет завершить свою componentDidMount() логику.
  3. Если wasInitialized - false, мы не будем перенаправлять на / login, вместо этого мы просто отобразим пустую строку, давая родительскому приложению componentDidMount() время выполнения и обновления isAuthenticated значение.
  4. Итак, теперь давайте посмотрим на эту строку:

    <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;

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...