React router v4 - авторизованные маршруты с HOC
У меня проблема с тем, чтобы предотвратить доступ неавторизованных пользователей к маршрутам / компонентам, разрешенным только для авторизации, например, к панели пользователя
У меня есть следующий код:
import React from 'react'
//other imports
import {withRouter} from 'react-router'
class User extends React.Component {
constructor(props) {
super(props)
console.log('props', props)
let user = JSON.parse(localStorage.getItem('userDetails'))
if(!user || !user.user || props.match.params.steamId !== user.user.steamId) {
props.history.push('/')
} else {
this.props.updateUserState(user)
this.props.getUser(user.user.steamId)
}
}
//render function
}
//mapStateToProps and mapDispatchToProps
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(User))
Роутер:
render() {
return (
<Router>
<div>
<Route exact path="/" component={Main}/>
<Route path="/user/:steamId" component={User}/>
<Route path="/completelogin" component={CompleteLogin}/>
</div>
</Router>
)
}
Я попытался войти, чтобы проверить, введено ли условие, но я получаю сообщение об ошибке, в котором говорится, что он не может прочитать свойства null.
Есть ли способ исправить мою проблему, а также лучший подход для удовлетворения моих требований? где конкретные компоненты будут строго доступны только авторизованным пользователям
2 ответа
Итак, согласно вашей логике, неавторизованным пользователям запрещен доступ к компоненту User. Просто и честно. С этим проблем нет.
Но меня беспокоит то, что вы проверяете, вошел ли пользователь в компонент, куда не должны входить неаутентифицированные пользователи. Это неверно, на мой взгляд, потому что:
Это дополнительная поездка для нашей программы - добавляет лишнюю ненужную неэффективность. Существует вероятность того, что с маршрутизатора мы переходим к компоненту User, а последний отправляет нас обратно к первому. Пинг-понг.
Пользовательский компонент выглядит грязным. Имея неуместную логику. Да не имеет значения. Поскольку проверка подлинности не должна выполняться в компоненте User. Пользовательский компонент должен содержать пользовательские материалы.
Как вы думаете, если вместо того, чтобы попасть внутрь компонента User для проверки аутентификации пользователей, мы проверим это в маршрутизаторе? Пользовательский компонент, как его называют, предназначен для пользователей, и логика для проверки аутентификации должна быть удалена оттуда.
Хорошо, это круто. Но как?
Мы можем создать компонент высшего порядка (HOC), который в качестве аргумента будет принимать любой компонент, переданный ему. Затем мы добавляем логику аутентификации в HOC и, наконец, в зависимости от используемой логики, мы можем либо перенаправить на домашнюю страницу, либо разрешить запрос к данному компоненту.
Для того, чтобы HOC мог выполнить вышеперечисленное, ему необходим доступ к:
- Государственный. Нам нужно знать, вошел ли пользователь в систему и в каком состоянии мы храним такие данные.
- Router. Возможно, нам нужно перенаправить пользователей.
Давайте назовем HOC required_auth
, Вот код этого:
import React, { Component } from 'react';
import { connect } from 'react-redux';
export default function(ComposedComponent) {
class Authentication extends Component {
static contextTypes = {
router: React.PropTypes.object
}
componentWillMount() {
if (!this.props.authenticated) {
this.context.router.history.push('/');
}
}
componentWillUpdate(nextProps) {
if (!nextProps.authenticated) {
this.context.router.history.push('/');
}
}
render() {
return <ComposedComponent {...this.props} />
}
}
function mapStateToProps(state) {
return { authenticated: state.auth.authed };
}
return connect(mapStateToProps)(Authentication);
}
Как видите, здесь нет чёрной магии. Что может быть запутанным, так это
static contextTypes = {
router: React.PropTypes.object
}
context
похож наprops
но это позволяет нам пропускать уровни в нашей иерархии компонентов
Так как this.context
React вынуждает нас определять контекст таким образом.
Не используйте контекст, если вы действительно не знаете, что делаете. Вариант использования для контекста не так распространен. Подробнее о том, какие могут быть последствия, читайте здесь
Чтобы сделать вывод о нашем HOC, он просто принимает компонент в качестве аргумента и либо перенаправляет его на домашнюю страницу, либо возвращает компонент, который мы передадим ему.
Теперь, чтобы использовать его, в файле маршрута мы импортируем HOC
import RequiredAuth from './components/auth/required_auth';
и любые маршруты, которые мы хотим защитить от неавторизованных пользователей, мы просто маршрутизируем так:
<Route path="/user" component={RequiredAuth(User)}/>
Строка выше будет либо направлена на домашнюю страницу, либо вернет компонент, который мы передаем, User
Ссылки: https://facebook.github.io/react/docs/higher-order-components.html https://facebook.github.io/react/docs/context.html
Поскольку вы используете React Router 4, ответ Мэтью Барбары, хотя и абсолютно правильный, излишне сложен. С RR4 вы можете просто использовать компонент Redirect для обработки ваших перенаправлений. См. https://reacttraining.com/react-router/web/api/Redirect для получения дополнительной информации.
import React, { Component } from "react";
import { connect } from "react-redux";
import { Redirect } from "react-router-dom";
export default function requireAuth (WrappedComponent) {
class Authentication extends Component {
render () {
if (!this.props.authenticated) {
return <Redirect to="/"/>
}
return <WrappedComponent { ...this.props }/>
}
}
function mapStateToProps (state) {
return { authenticated: state.authenticated }
}
return connect(mapStateToProps)(Authentication);
}