Подтвердить что ты не робот

Доступ к хранилищу Redux с маршрутов, настроенных с помощью React Router

Я хотел бы использовать обработчик response-router onEnter, чтобы побудить пользователей аутентифицироваться при вводе ограниченного маршрута.

Пока мой файл routes.js выглядит примерно так:

import React from 'react';
import { Route, IndexRoute } from 'react-router';

export default (
    <Route   path="/"         component={App}>
      <IndexRoute             component={Landing} />
      <Route path="learn"     component={Learn} />
      <Route path="about"     component={About} />
      <Route path="downloads" component={Downloads} onEnter={requireAuth} />
    </Route>
)

В идеале, я бы хотел, чтобы моя функция requireAuth была действием сокращения, которое имеет доступ к хранилищу и текущему состоянию, которое работает следующим образом: store.dispatch(requireAuth()).

К сожалению, у меня нет доступа к хранилищу в этом файле. Я не думаю, что могу использовать действительно connect в этом случае для доступа к соответствующим действиям, которые я хочу. Я также не могу просто import store из файла, где создается хранилище, поскольку это undefined, когда приложение загружается первым.

4b9b3361

Ответ 1

Самый простой способ добиться этого - передать ваш магазин функции, которая возвращает ваши маршруты (а не возвращать маршруты напрямую). Таким образом, вы можете получить доступ к хранилищу в onEnter и других методах реагирования маршрутизатора.

Итак, для ваших маршрутов:

import React from 'react';
import { Route, IndexRoute } from 'react-router';

export const getRoutes = (store) => (
  const authRequired = (nextState, replaceState) => {
    // Now you can access the store object here.
    const state = store.getState();

    if (!state.user.isAuthenticated) {
      // Not authenticated, redirect to login.
      replaceState({ nextPathname: nextState.location.pathname }, '/login');
    }
  };

  return (
    <Route   path="/"         component={App}>
      <IndexRoute             component={Landing} />
      <Route path="learn"     component={Learn} />
      <Route path="about"     component={About} />
      <Route path="downloads" component={Downloads} onEnter={authRequired} />
    </Route>
  );
)

Затем обновите свой основной компонент, чтобы вызвать функцию getRoutes, передав в хранилище:

<Provider store={ store }>
  <Router history={ history }>
    { getRoutes(store) }
  </Router>
</Provider>

Что касается отправки действия из requireAuth, вы можете написать свою функцию следующим образом:

const authRequired = (nextState, replaceState, callback) => {
  store.dispatch(requireAuth())  // Assume this action returns a promise
    .then(() => {
      const state = store.getState();

      if (!state.user.isAuthenticated) {
        // Not authenticated, redirect to login.
        replaceState({ nextPathname: nextState.location.pathname }, '/login');
      }

      // All ok
      callback();
    });
};

Надеюсь, что это поможет.

Ответ 2

Если вы хотите, чтобы вы могли написать route.js следующим образом:

var requireAuth = (store, nextState, replace) => {
  console.log("store: ", store);
  //now you have access to the store in the onEnter hook!
}

export default (store) => {
  return (
      <Route path="/"           component={App}>
        <IndexRoute             component={Landing} />
        <Route path="learn"     component={Learn} />
        <Route path="about"     component={About} />
        <Route path="downloads" component={Downloads} onEnter={requireAuth.bind(this, store)} />
      </Route>
    );
);

Я установил пример, с которым вы могли бы играть в этом codepen.

Не уверен, что инициирование действия для обработки auth - хорошая идея. Лично я предпочитаю обрабатывать auth в другим способом:

Вместо использования крюка onEnter я использую функцию обертки. Я хочу, чтобы раздел администратора моего блога был защищен, поэтому я завернул компонент AdminContainer в маршрутах с помощью функции requireAuthentication, см. Ниже.

export default (store, history) => {
        return (
            <Router history={history}>
                <Route path="/" component={App}>
                    { /* Home (main) route */ }
                    <IndexRoute component={HomeContainer}/>
                    <Route path="post/:slug" component={PostPage}/>
                    { /* <Route path="*" component={NotFound} status={404} /> */ }
                </Route>

                <Route path="/admin" component={requireAuthentication(AdminContainer)}>
                    <IndexRoute component={PostList}/>
                    <Route path=":slug/edit" component={PostEditor}/>
                    <Route path="add" component={PostEditor}/>
                </Route>
                <Route path="/login" component={Login}/>
            </Router>
        );
    };

requireAuthentication - функция, которая

  • если пользователь аутентифицирован, отображает завернутый компонент,
  • иначе перенаправляется на Login

Вы можете увидеть это ниже:

export default function requireAuthentication(Component) {
    class AuthenticatedComponent extends React.Component {

        componentWillMount () {
            this.checkAuth();
        }

        componentWillReceiveProps (nextProps) {
            this.checkAuth();
        }

        checkAuth () {
            if (!this.props.isAuthenticated) {
                let redirectAfterLogin = this.props.location.pathname;
                this.context.router.replace({pathname: '/login', state: {redirectAfterLogin: redirectAfterLogin}});
            }
        }

        render () {
            return (
                <div>
                    {this.props.isAuthenticated === true
                        ? <Component {...this.props}/>
                        : null
                    }
                </div>
            )

        }
    }

    const mapStateToProps = (state) => ({
        isAuthenticated: state.blog.get('isAuthenticated')
    });

    AuthenticatedComponent.contextTypes = {
        router: React.PropTypes.object.isRequired
    };

    return connect(mapStateToProps)(AuthenticatedComponent);
}

Кроме того, requireAuthentication будет защищать все маршруты в /admin. И вы можете использовать его везде, где захотите.

Ответ 3

Лоты изменились с течением времени. onEnter больше не существует на react-router-4

Ниже приведен мой реальный проект для вашей справки

export const getRoutes = (store) => {
  const PrivateRoute = ({ component: Component, ...rest }) => (
    <Route {...rest} render={props => (
      checkIfAuthed(store) ? (
        <Component {...props}/>
      ) : (
        <Redirect to={{
          pathname: '/login'
        }}/>
      )
    )}/>
  )

  return (
    <Router>
      <div>
        <PrivateRoute exact path="/" component={Home}/>
        <Route path="/login" component={Login} />
      </div>
    </Router>
  )
}