React HOC: ошибка перенаправления при смене типа аккаунта - PullRequest
1 голос
/ 16 апреля 2019

Я создал компонент более высокого порядка WithAccess, чтобы проверить, есть ли у пользователя права (авторизованы и роль учетной записи) для входа в некоторые защищенные маршруты. Я использую HOC, как это:

const authCondition = authUser => !!authUser;

<Route path="/a" component={WithAccess(authCondition, "admin")(Admin)} />
<Route path="/u" component={WithAccess(authCondition, "user")(User)} />

Администратор и Пользователь - два функциональных компонента с маршрутами.

WithAccess содержит прослушиватель onAuthStateChanged. Внутри слушателя я проверяю роль пользователя (я настраиваю настраиваемую претензию «роль» при создании пользователя). Если это соответствует пропу 'ролью', которая была передана, isLoading устанавливается в false и компонент будет визуализироваться. В противном случае пользователь будет перенаправлен обратно на страницу входа.

const WithAccess = (condition, role) => Component => {
  class withAccess extends React.Component {
    state = {
      isLoading: true
    };

    componentDidMount() {
      Auth.onAuthStateChanged(async user => {
        if (user) {
          const idTokenResult = await user.getIdTokenResult(true);
          if (condition(user) && idTokenResult.claims.role === role)
            this.setState({ isLoading: false });
          else this.props.history.push(`/login`);
        }
      });
    }

    render() {
      return !this.state.isLoading ? (
        <Component />
      ) : (
        <PageLoader label="Checking user..." />
      );
    }
  }

  return withRouter(withAccess);
};

export default WithAccess;

Это работает. Однако, когда я переключаюсь с учетной записи администратора на учетную запись пользователя или наоборот, WithAccess передает предыдущую роль.

Чтобы прояснить ситуацию, я переделал часть входа / регистрации в песочнице кода: ссылка на песочницу кода .

Лучший способ воспроизвести:

  1. Перейти на страницу входа и зарегистрировать нового пользователя
  2. Выход из системы при перенаправлении на панель пользователя.
  3. Войдите в систему как администратор с электронной почтой: admin@example.com и паролем: 123456
  4. «Логин» изменится на «Панель инструментов» в навигации, но вы останетесь на странице входа в систему (на самом деле вы перенаправлены на / a / панель инструментов, но WithAccess немедленно перенаправляет вас обратно)

Я пытаюсь понять, почему WithAccess передает предыдущую роль при переключении с типа учетной записи, но пока не может понять это.

Ответы [ 2 ]

1 голос
/ 16 апреля 2019

В вашем приложении есть утечка памяти! Довольно сложно поймать их :) https://codesandbox.io/s/k3218vqq8o

В вашем With Access HOC вы должны отказаться от обратного вызова в firebase onAuth.

Всегда не забывайте очищать компонент, если вы добавляете слушателя где-нибудь!

componentDidMount() {
  //Retain a reference to the unlistener callback
  this.fireBaseAuthUnlistener = Auth.onAuthStateChanged(async user => {
    if (user) {
      const idTokenResult = await user.getIdTokenResult(true);
      console.log("ID Token claims: ", idTokenResult.claims);
      console.log("ROLE : ", role);
      if (condition(user) && idTokenResult.claims.role === role) {
        this.setState({ isLoading: false });
        console.log(user);
      } else {
        this.props.history.push(`/login`);
      }
    }
  });
}

componentWillUnmount() {
  //Unlisten when unmounting!
  this.fireBaseAuthUnlistener && this.fireBaseAuthUnlistener();
  this.fireBaseAuthUnlistener = undefined;
}
0 голосов
/ 16 апреля 2019

Я думаю, вам следует значительно упростить структуру маршрутов, где вы сначала включаете свои аутентифицированные маршруты в ProtectedRoutes HOC.Этот маршрут позволяет только авторизованным (авторизованным) пользователям просматривать его.Затем добавьте еще один RequireAdmin HOC, чтобы проверить, равен ли role admin.Если нет, он перенаправляет на страницу NotFound.

Аутентифицированный пользователь имеет доступ к /u/dashboard, но когда он щелкает ссылку Admin Dashboard, он показывает страницу NotFound.Однако администратор имеет доступ к обоим!

Для проверки:

  • нажмите Login
  • , войдите как testuser123@example.com с паролем 123456
  • при перенаправлении попробуйте получить доступ к Admin Dashboard
  • перенаправлениям на NotFound страницу
  • нажмите Go Back
  • нажмите Dashboard
  • нажмите Logout
  • нажмите Login еще раз.
  • войдите как admin@example.com с паролем 123456
  • при перенаправлении нажмите User Dashboard и Admin Dashboard (обе работы)

Рабочий пример :

Edit Protected Routes Example


контейнеры / ProtectedRoutes

import isEmpty from "lodash/isEmpty";
import React, { Component, Fragment } from "react";
import { connect } from "react-redux";
import { withRouter } from "react-router-dom";
import Navigation from "../../components/Website/Navigation";

class ProtectedRoutes extends Component {
  componentDidMount = () => {
   if (isEmpty(this.props.user) || !this.props.user.email) {
      this.props.history.push("/notfound");
    }
  };

  componentDidUpdate = prevProps => {
    if (this.props.user !== prevProps.user) {
      this.props.history.push("/notfound");
    }
  };

  render = () =>
    this.props.user.email ? (
      <Fragment>
        <Navigation />
        {this.props.children}
      </Fragment>
    ) : null;
}

export default connect(state => ({ user: state.user }))(
  withRouter(ProtectedRoutes)
);

контейнеры / RequireAdmin

import isEmpty from "lodash/isEmpty";
import { Component } from "react";
import { connect } from "react-redux";
import { withRouter } from "react-router-dom";

class RequireAdmin extends Component {
  componentDidMount = () => {
    if (isEmpty(this.props.user) || this.props.user.role !== "admin") {
      this.props.history.push("/notfound");
    }
  };

  componentDidUpdate = prevProps => {
    if (this.props.user !== prevProps.user) {
      this.props.history.push("/notfound");
    }
  };

  render = () =>
    isEmpty(this.props.user) || this.props.user.role === "admin"
      ? this.props.children
      : null;
}

export default connect(state => ({ user: state.user }))(
  withRouter(RequireAdmin)
);

маршруты (основной)

import React from "react";
import { Route, Switch } from "react-router-dom";
import ProtectedRoutes from "../containers/ProtectedRoutes";

import Admin from "../components/Admin";
import Home from "../pages/Website/Home";
import NotFound from "../pages/Website/NotFound";
import LoginForm from "../containers/LoginForm";
import RegisterForm from "../containers/RegisterForm";
import User from "../components/User";

const Routes = () => (
  <div>
    <Switch>
      <Route exact path="/" component={Home} />
      <Route exact path="/login" component={LoginForm} />
      <Route exact path="/register" component={RegisterForm} />
      <Route exact path="/notfound" component={NotFound} />
      <ProtectedRoutes>
        <Route path="/u" component={User} />
        <Route path="/a" component={Admin} />
      </ProtectedRoutes>
    </Switch>
  </div>
);

export default Routes;

компоненты / пользователь (маршруты, защищенные пользователем)

import React, { Fragment } from "react";
import { Route, Switch } from "react-router-dom";
import Dashboard from "../../pages/User/Dashboard";
import NotFound from "../../pages/Website/NotFound";

const User = ({ match }) => (
  <Fragment>
    <Switch>
      <Route exact path={`${match.url}/dashboard`} component={Dashboard} />
      <Route path={`${match.url}`} component={NotFound} />
    </Switch>
  </Fragment>
);

export default User;

компоненты / администратор (маршруты, защищенные администратором)

import React, { Fragment } from "react";
import { Route, Switch } from "react-router-dom";
import RequireAdmin from "../../containers/RequireAdmin";
import Dashboard from "../../pages/Admin/Dashboard";
import NotFound from "../../pages/Website/NotFound";

const Admin = ({ match }) => (
  <RequireAdmin>
    <Switch>
      <Route exact path={`${match.url}/dashboard`} component={Dashboard} />
      <Route path={`${match.url}`} component={NotFound} />
    </Switch>
  </RequireAdmin>
);

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