reactjs – 如何使用Apollo Client React Router根据用户状态实
我正在使用React Router 4进行路由,使用Apollo Client进行数据获取&缓存.我需要根据以下标准实施PrivateRoute和重定向解决方案:
>允许用户查看的页面基于用户状态,可以从服务器获取,也可以从缓存中读取.用户状态本质上是一组标志,用于了解用户在我们的渠道中的位置.示例标志:isLoggedIn,isOnboarded,isWaitlisted等. 总之,我们需要一个给用户状态数据的库,可以 >计算用户是否可以在某个页面上 我已经在开发一个通用库,但它现在有它的缺点.我正在寻求关于如何解决这个问题的意见,以及是否有既定的模式来实现这一目标. 这是我目前的做法.这不起作用,因为getRedirectPath需要的数据位于OnboardingPage组件中. 此外,我无法将PrivateRoute包装为可以注入计算重定向路径所需的道具的HOC,因为这不会让我将其用作Switch React Router组件的子项,因为它不再是Route. <PrivateRoute exact path="/onboarding" isRender={(props) => { return props.userStatus.isLoggedIn && props.userStatus.isWaitlistApproved; }} getRedirectPath={(props) => { if (!props.userStatus.isLoggedIn) return '/login'; if (!props.userStatus.isWaitlistApproved) return '/waitlist'; }} component={OnboardingPage} />
一般的做法
我会创建一个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' })` // ...and returns a new component wrapping the parameter component return hoistNonReactStatics(Private,WrappedComponent) } export default privateRoute 然后,您只需要更改导出路线的方式: export default privateRoute({ requireLoggedIn: true })(MyRoute); 并且您可以像在今天的react-router中那样使用该路由: <Route path="/" component={MyPrivateRoute} /> 重定向逻辑 如何设置此部分取决于几个因素: >如何确定用户是否已登录,登机,等候等. 处理用户状态 既然你正在使用Apollo,你可能只想使用graphql来获取你的HOC中的数据: return hoistNonReactStatics( graphql(gql` query ... `)(Private),WrappedComponent ) 然后你可以修改私有组件来获取这些道具: class Private extends Component { componentDidMount() { const { userStatus: { isLoggedIn,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,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 = ({ requireLogedIn = false,pathIfNotLoggedIn = '/a/sensible/default',// ... }) // ... 然后,如果要覆盖默认路径,请将导出更改为: export default privateRoute({ requireLoggedIn: true,pathIfNotLoggedIn: '/a/specific/page' })(MyRoute) 路线是负责任的 如果您希望能够从路由传递路径,您将希望在私有中接收这些路径 class Private extends Component { componentDidMount() { const { userStatus: { isLoggedIn,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,// we don't care about these for rendering,but we don't want to pass them to WrappedComponent pathIfNotLoggedIn,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,requireWaitlisted = false,pathIfNotLoggedIn = '/login',pathIfNotOnboarded = '/onboarding',pathIfNotWaitlisted = '/waitlist' } = {}) => WrappedComponent => { class Private extends Component { componentDidMount() { const { userStatus: { isLoggedIn,isWaitlisted },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,...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,pathIfNotWaitlisted } Private.displayName = `Private(${ WrappedComponent.displayName || WrappedComponent.name || 'Component' })` return hoistNonReactStatics( graphql(gql` query ... `)(Private),WrappedComponent ) } export default privateRoute 我按照the official documentation的建议使用hoist-non-react-statics. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |