reactjs – 如何使用Apollo Client React Router根据用户状态实现私有路由和重定向?

我正在使用React Router 4进行路由,使用Apollo Client进行数据获取&缓存.我需要根据以下标准实施PrivateRoute和重定向解决方案:

>允许用户查看的页面基于用户状态,可以从服务器获取,也可以从缓存中读取.用户状态本质上是一组标志,用于了解用户在我们的渠道中的位置.示例标志:isLoggedIn,isOnboarded,isWaitlisted等.
>如果用户的状态不允许他们在该页面上,则不应该开始呈现页面.例如,如果您不是isWaitlisted,则不应该看到等待列表页面.当用户意外地发现自己在这些页面上时,应将其重定向到适合其状态的页面.
>重定向也应该是动态的.例如,假设您在isLoggedIn之前尝试查看用户配置文件.然后我们需要将您重定向到登录页面.但是,如果您是isLoggedIn但不是isOnboarded,我们仍然不希望您看到您的个人资料.因此,我们希望将您重定向到新手入门页面.
>所有这些都需要在路线层面上进行.页面本身应该不知道这些权限&重定向.

总之,我们需要一个给用户状态数据的库,可以

>计算用户是否可以在某个页面上
>计算需要动态重定向的位置
>在渲染任何页面之前执行这些操作
>在路线级别执行这些操作

我已经在开发一个通用库,但它现在有它的缺点.我正在寻求关于如何解决这个问题的意见,以及是否有既定的模式来实现这一目标.

这是我目前的做法.这不起作用,因为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.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom