Основной подход
Я бы создал HOC для обработки этой логики для всех ваших страниц.
// privateRoute is a function...
const privateRoute = ({
// ...that takes optional boolean parameters...
requireLoggedIn = false,
requireOnboarded = false,
requireWaitlisted = false
// ...and returns a function that takes a component...
} = {}) => WrappedComponent => {
class Private extends Component {
componentDidMount() {
// redirect logic
}
render() {
if (
(requireLoggedIn && /* user isn't logged in */) ||
(requireOnboarded && /* user isn't onboarded */) ||
(requireWaitlisted && /* user isn't waitlisted */)
) {
return null
}
return (
<WrappedComponent {...this.props} />
)
}
}
Private.displayName = `Private(${
WrappedComponent.displayName ||
WrappedComponent.name ||
'Component'
})`
hoistNonReactStatics(Private, WrappedComponent)
// ...and returns a new component wrapping the parameter component
return Private
}
export default privateRoute
Затем вам нужно только изменить способ экспорта маршрутов:
export default privateRoute({ requireLoggedIn: true })(MyRoute);
и вы можете использовать этот маршрут так же, как сегодня, в response-router:
<Route path="/" component={MyPrivateRoute} />
Логика перенаправления
Как вы установите эту часть, зависит от пары факторов:
- Как вы определяете, вошел ли пользователь в систему, включен, находится ли он в списке ожидания и т. Д.
- Какой компонент вы хотите отвечать за куда для перенаправления.
Обработка статуса пользователя
Поскольку вы используете Apollo, вы, вероятно, просто захотите использовать graphql
, чтобы получить эти данные в вашем HOC:
return graphql(gql`
query ...
`)(Private)
Затем вы можете изменить компонент Private
, чтобы получить эти реквизиты:
class Private extends Component {
componentDidMount() {
const {
userStatus: {
isLoggedIn,
isOnboarded,
isWaitlisted
}
} = this.props
if (requireLoggedIn && !isLoggedIn) {
// redirect somewhere
} else if (requireOnboarded && !isOnboarded) {
// redirect somewhere else
} else if (requireWaitlisted && !isWaitlisted) {
// redirect to yet another location
}
}
render() {
const {
userStatus: {
isLoggedIn,
isOnboarded,
isWaitlisted
},
...passThroughProps
} = this.props
if (
(requireLoggedIn && !isLoggedIn) ||
(requireOnboarded && !isOnboarded) ||
(requireWaitlisted && !isWaitlisted)
) {
return null
}
return (
<WrappedComponent {...passThroughProps} />
)
}
}
Куда перенаправить
Есть несколько разных способов справиться с этим.
Простой способ: маршруты статичны
Если пользователь не вошел в систему, вы всегда хотите перенаправить на /login?return=${currentRoute}
.
В этом случае вы можете просто жестко запрограммировать эти маршруты в своем componentDidMount
. Выполнено.
Компонент отвечает
Если вы хотите, чтобы ваш MyRoute
компонент определял путь, вы можете просто добавить некоторые дополнительные параметры в свою privateRoute
функцию, а затем передать их при экспорте MyRoute
.
const privateRoute = ({
requireLoggedIn = false,
pathIfNotLoggedIn = '/a/sensible/default',
// ...
}) // ...
Затем, если вы хотите переопределить путь по умолчанию, вы измените свой экспорт на:
export default privateRoute({
requireLoggedIn: true,
pathIfNotLoggedIn: '/a/specific/page'
})(MyRoute)
Маршрут отвечает
Если вы хотите иметь возможность проходить по пути от маршрутизации, вы захотите получить для них реквизиты в Private
class Private extends Component {
componentDidMount() {
const {
userStatus: {
isLoggedIn,
isOnboarded,
isWaitlisted
},
pathIfNotLoggedIn,
pathIfNotOnboarded,
pathIfNotWaitlisted
} = this.props
if (requireLoggedIn && !isLoggedIn) {
// redirect to `pathIfNotLoggedIn`
} else if (requireOnboarded && !isOnboarded) {
// redirect to `pathIfNotOnboarded`
} else if (requireWaitlisted && !isWaitlisted) {
// redirect to `pathIfNotWaitlisted`
}
}
render() {
const {
userStatus: {
isLoggedIn,
isOnboarded,
isWaitlisted
},
// we don't care about these for rendering, but we don't want to pass them to WrappedComponent
pathIfNotLoggedIn,
pathIfNotOnboarded,
pathIfNotWaitlisted,
...passThroughProps
} = this.props
if (
(requireLoggedIn && !isLoggedIn) ||
(requireOnboarded && !isOnboarded) ||
(requireWaitlisted && !isWaitlisted)
) {
return null
}
return (
<WrappedComponent {...passThroughProps} />
)
}
}
Private.propTypes = {
pathIfNotLoggedIn: PropTypes.string
}
Private.defaultProps = {
pathIfNotLoggedIn: '/a/sensible/default'
}
Тогда ваш маршрут можно переписать на:
<Route path="/" render={props => <MyPrivateComponent {...props} pathIfNotLoggedIn="/a/specific/path" />} />
Комбинировать варианты 2 и 3
(Это подход, который мне нравится использовать)
Вы также можете позволить компоненту и маршруту выбирать, кто несет ответственность. Вам просто нужно добавить privateRoute
параметры для путей, как мы сделали, чтобы позволить компоненту решать. Затем используйте эти значения как свои defaultProps
, как мы это делали, когда был ответственным за маршрут.
Это дает вам возможность принимать решения по ходу дела. Просто обратите внимание, что передача маршрутов в качестве свойств будет иметь приоритет перед передачей из компонента в HOC.
Все вместе сейчас
Вот фрагмент, объединяющий все концепции, приведенные выше, для окончательного рассмотрения HOC:
const privateRoute = ({
requireLoggedIn = false,
requireOnboarded = false,
requireWaitlisted = false,
pathIfNotLoggedIn = '/login',
pathIfNotOnboarded = '/onboarding',
pathIfNotWaitlisted = '/waitlist'
} = {}) => WrappedComponent => {
class Private extends Component {
componentDidMount() {
const {
userStatus: {
isLoggedIn,
isOnboarded,
isWaitlisted
},
pathIfNotLoggedIn,
pathIfNotOnboarded,
pathIfNotWaitlisted
} = this.props
if (requireLoggedIn && !isLoggedIn) {
// redirect to `pathIfNotLoggedIn`
} else if (requireOnboarded && !isOnboarded) {
// redirect to `pathIfNotOnboarded`
} else if (requireWaitlisted && !isWaitlisted) {
// redirect to `pathIfNotWaitlisted`
}
}
render() {
const {
userStatus: {
isLoggedIn,
isOnboarded,
isWaitlisted
},
pathIfNotLoggedIn,
pathIfNotOnboarded,
pathIfNotWaitlisted,
...passThroughProps
} = this.props
if (
(requireLoggedIn && !isLoggedIn) ||
(requireOnboarded && !isOnboarded) ||
(requireWaitlisted && !isWaitlisted)
) {
return null
}
return (
<WrappedComponent {...passThroughProps} />
)
}
}
Private.propTypes = {
pathIfNotLoggedIn: PropTypes.string,
pathIfNotOnboarded: PropTypes.string,
pathIfNotWaitlisted: PropTypes.string
}
Private.defaultProps = {
pathIfNotLoggedIn,
pathIfNotOnboarded,
pathIfNotWaitlisted
}
Private.displayName = `Private(${
WrappedComponent.displayName ||
WrappedComponent.name ||
'Component'
})`
hoistNonReactStatics(Private, WrappedComponent)
return graphql(gql`
query ...
`)(Private)
}
export default privateRoute
Я использую hoist-non-react-statics, как предложено в официальную документацию.
person
Luke
schedule
15.02.2018