Страница входа в систему не отображается с помощью защищенного маршрутизатора React

#reactjs #authentication #react-router

#reactjs #аутентификация #react-маршрутизатор

Вопрос:

У меня есть приложение react router:

     export default () => (
  <Router basename={process.env.REACT_APP_BASENAME || ""}>
    <div>
      {routes.map((route, index) => {
        return (
          <PrivateRoute
            key={index}
            path={route.path}
            exact={route.exact}
            component={props => {
              return (
                <route.layout {...props}>
                  <route.component {...props} />
                </route.layout>
              );
            }}
          />
        );
      })}
    </div>
  </Router>
);
  

и это приведет к отображению разных представлений на основе выбранного маршрута. маршруты будут отображаться на основе этого объекта в routes.js файле:

     export default [
        {
    path: "/login",
    layout: DefaultLayout,
    component: LogIn
  },    .....]
  

Чтобы встроить некоторую аутентификацию, я определил PrivateRoute как:

     const PrivateRoute = ({ component: Component, ...rest }) => (
  <Route {...rest} render={(props) => (
    fakeAuth.isAuthenticated === true
      ? <Component {...props} />
      : <Redirect to='/login' />
  )} />
)
  

однако, когда я настраиваю приложение на использование PrivateRoute вместо обычного маршрута (в первом фрагменте), перенаправление не использует объект routes. Как мне изменить константу PrivateRoute для страницы входа в систему, отражающую мою исходную архитектуру маршрута React? какова наилучшая практика?

Ответ №1:

Ваш код выглядит нормально, но поскольку вы сказали, что ваш routes объект не понят react-router, возможно, ваши компоненты определены неправильно. Например, ваши компоненты могут быть определены после создания объекта. В этом случае, когда этот объект создается, он будет ссылаться на undefined компоненты. Я однажды допустил эту ошибку, поэтому я просто делюсь тем, что, возможно, пошло не так.

Вот пример:

 import ReactDOM from "react-dom";
import React, { Component } from "react";
import {
  BrowserRouter as Router,
  Route,
  Link,
  Redirect,
  withRouter
} from "react-router-dom";

function Public() {
  return <h3>Public</h3>;
}

function Protected() {
  return <h3>You can see protected content</h3>;
}

class Login extends Component {
  state = { redirectToReferrer: false };

  login = () => {
    fakeAuth.authenticate(() => {
      this.setState({ redirectToReferrer: true });
    });
  };

  render() {
    let { from } = this.props.location.state || { from: { pathname: "/" } };
    let { redirectToReferrer } = this.state;

    if (redirectToReferrer) return <Redirect to={from} />;

    return (
      <div>
        <p>You must log in to view the page at {from.pathname}</p>
        <button onClick={this.login}>Log in</button>
      </div>
    );
  }
}

const routes = [
  {
    path: "/public",
    component: Public,
    private: false
  },
  {
    path: "/login",
    component: Login,
    private: false
  },
  {
    path: "/protected",
    component: Protected,
    private: true
  }
];

function AuthExample() {
  return (
    <Router>
      <div>
        <AuthButton />
        <ul>
          <li>
            <Link to="/public">Public Page</Link>
          </li>
          <li>
            <Link to="/protected">Protected Page</Link>
          </li>
        </ul>
        {routes.map((route, index) => {
          if (route.private)
            return (
              <PrivateRoute
                key={index}
                path={route.path}
                exact={route.exact}
                component={props => {
                  return <route.component {...props} />;
                }}
              />
            );

          return (
            <Route
              key={index}
              path={route.path}
              exact={route.exact}
              component={props => {
                return <route.component {...props} />;
              }}
            />
          );
        })}
      </div>
    </Router>
  );
}

const fakeAuth = {
  isAuthenticated: false,
  authenticate(cb) {
    this.isAuthenticated = true;
    setTimeout(cb, 100); // fake async
  },
  signout(cb) {
    this.isAuthenticated = false;
    setTimeout(cb, 100);
  }
};

const AuthButton = withRouter(({ history }) =>
  fakeAuth.isAuthenticated ? (
    <p>
      Welcome!{" "}
      <button
        onClick={() => {
          fakeAuth.signout(() => history.push("/"));
        }}
      >
        Sign out
      </button>
    </p>
  ) : (
    <p>You are not logged in.</p>
  )
);

function PrivateRoute(props) {
  const { component: Component, ...rest } = props;
  return (
    <Route
      {...rest}
      render={props =>
        fakeAuth.isAuthenticated ? (
          <Component {...props} />
        ) : (
          <Redirect
            to={{
              pathname: "/login",
              state: { from: props.location }
            }}
          />
        )
      }
    />
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<AuthExample />, rootElement);
  

Обратите внимание, что Public , Protected и Login компоненты определены над routes объектом. Их определение после routes приведет к ошибкам.

Ответ №2:

Я предлагаю изменить ваш частный маршрут следующим образом

 const PrivateRoute = ({ component: Component, ...rest }) => fakeAuth.isAuthenticated === true ? (
  <Route {...rest} component={component}
  )} />
) : <Redirect to='/login' />;