Доступ к хранилищу 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, когда приложение загружается первым.
Ответы
Ответ 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>
)
}