Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/reactjs/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Reactjs 如何使用Apollo客户端+;根据用户状态对路由器进行响应以实现私有路由和重定向?_Reactjs_React Router_Apollo_React Apollo_Apollo Client - Fatal编程技术网

Reactjs 如何使用Apollo客户端+;根据用户状态对路由器进行响应以实现私有路由和重定向?

Reactjs 如何使用Apollo客户端+;根据用户状态对路由器进行响应以实现私有路由和重定向?,reactjs,react-router,apollo,react-apollo,apollo-client,Reactjs,React Router,Apollo,React Apollo,Apollo Client,我使用React路由器4进行路由,Apollo客户端进行数据获取和缓存。我需要根据以下标准实施PrivateRoute和重定向解决方案: 允许用户查看的页面基于其用户状态,用户状态可以从服务器获取,也可以从缓存读取。用户状态本质上是一组我们用来了解用户在漏斗中的位置的标志。示例标志:isLoggedIn,isOnboarded,isWaitlisted等 如果用户的状态不允许他们出现在页面上,则任何页面都不应该开始呈现。例如,如果您不是iswaitlist,则不应该看到waitlist页面。当用

我使用React路由器4进行路由,Apollo客户端进行数据获取和缓存。我需要根据以下标准实施PrivateRoute和重定向解决方案:

  • 允许用户查看的页面基于其用户状态,用户状态可以从服务器获取,也可以从缓存读取。用户状态本质上是一组我们用来了解用户在漏斗中的位置的标志。示例标志:
    isLoggedIn
    isOnboarded
    isWaitlisted

  • 如果用户的状态不允许他们出现在页面上,则任何页面都不应该开始呈现。例如,如果您不是
    iswaitlist
    ,则不应该看到waitlist页面。当用户意外发现自己在这些页面上时,应将其重定向到适合其状态的页面

  • 重定向也应该是动态的。例如,假设您在
    isLoggedIn
    之前尝试查看您的用户配置文件。然后我们需要将您重定向到登录页面。但是,如果您是
    isLoggedIn
    而不是
    isOnboarded
    ,我们仍然不希望您看到您的个人资料。因此,我们想将您重定向到入职页面

  • 所有这些都需要在路线层面上进行。页面本身应该不知道这些权限和重定向

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

    • 计算用户是否可以在某个页面上
    • 计算需要动态重定向到的位置
    • 在呈现任何页面之前执行这些操作
    • 在路线级别执行这些操作
    我已经在开发一个通用库,但它现在有它的缺点。我正在寻求关于如何处理这个问题的意见,以及是否有既定的模式来实现这个目标

    这是我目前的做法。这不起作用,因为
    getRedirectPath
    所需的数据位于
    OnboardingPage组件中

    另外,我不能用能够注入计算重定向路径所需的道具的HOC来包装PrivateRoute,因为这不允许我将其用作Switch-React路由器组件的子组件,因为它不再是路由

    <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}
    />
    
    {
    返回props.userStatus.isLoggedIn&&props.userStatus.iswaitlistproved;
    }}
    getRedirectPath={(道具)=>{
    如果(!props.userStatus.isLoggedIn)返回“/login”;
    如果(!props.userStatus.iswaitlistproved)返回“/waitlist”;
    }}
    组件={OnboardingPage}
    />
    
    我认为您需要将逻辑向下移动一点。比如:

    <Route path="/onboarding" render={renderProps=>
       <CheckAuthorization authorized={OnBoardingPage} renderProps={renderProps} />
    }/>
    
    
    }/>
    
    您必须使用不带“react-graphql”HOC的Apollo客户端。
    1.获取Apollo客户端的实例
    2.火灾查询
    3.而查询返回数据渲染加载..
    4.根据数据检查并授权路线。
    5.返回适当的组件或重定向

    这可以通过以下方式完成:

    import Loadable from 'react-loadable'
    import client from '...your ApolloClient instance...'
    
    const queryPromise = client.query({
            query: Storequery,
            variables: {
                name: context.params.sellername
            }
        })
    const CheckedComponent = Loadable({
      loading: LoadingComponent,
      loader: () => new Promise((resolve)=>{
           queryPromise.then(response=>{
             /*
               check response data and resolve appropriate component.
               if matching error return redirect. */
               if(response.data.userStatus.isLoggedIn){
                resolve(ComponentToBeRendered)
               }else{
                 resolve(<Redirect to={somePath}/>)
               }
           })
       }),
    }) 
    <Route path="/onboarding" component={CheckedComponent} />
    
    从“react Loadable”导入可加载
    从“…您的客户端实例…”导入客户端
    const queryPromise=client.query({
    查询:Storequery,
    变量:{
    名称:context.params.sellername
    }
    })
    常量CheckedComponent=可加载({
    加载:加载组件,
    加载器:()=>新承诺((解决)=>{
    然后(响应=>{
    /*
    检查响应数据并解析相应的组件。
    如果匹配错误,返回重定向*/
    if(response.data.userStatus.isLoggedIn){
    解析(组件被引用)
    }否则{
    解决()
    }
    })
    }),
    }) 
    
    相关API参考:

    我个人通常会这样构建我的私人路线:

    const renderMergedProps = (component, ...rest) => {
      const finalProps = Object.assign({}, ...rest);
      return React.createElement(component, finalProps);
    };
    
    const PrivateRoute = ({
      component, redirectTo, path, ...rest
    }) => (
      <Route
        {...rest}
        render={routeProps =>
          (loggedIn() ? (
            renderMergedProps(component, routeProps, rest)
          ) : (
            <Redirect to={redirectTo} from={path} />
          ))
        }
      />
    );
    
    PrivateRoute
    中的所有子例程首先需要检查用户是否已登录

    最后一步是根据所需状态嵌套路由。

    常规方法 我将创建一个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
    
    您可以像今天在react router中一样使用该路由:

    <Route path="/" component={MyPrivateRoute} />
    
    然后您可以修改
    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} />
        )
      }
    }
    
    然后,如果要覆盖默认路径,请将导出更改为:

    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'
    }
    

    如果您使用的是apollo react客户端,您还可以导入
    查询
    @apollo/components
    ,并在您的专用路径中使用它:

        <Query query={fetchUserInfoQuery(moreUserInfo)}>
          {({ loading, error, data: userInfo = {} }: any) => {
            const isNotAuthenticated = !loading && (isEmpty(userInfo) || !userInfo.whoAmI);
    
            if (isNotAuthenticated || error) {
              return <Redirect to={RoutesPaths.Login} />;
            }
            const { whoAmI } = userInfo;
    
            return <Component user={whoAmI} {...renderProps} />;
          }}
        </Query>
    

    你好,卢克,你也有时间来回答我的问题吗?你好,Dyo,你有时间也来看看我的问题吗?你好,你也有时间来看看我的问题吗?你好,你也有时间来看看我的问题吗?
    const privateRoute = ({
      requireLoggedIn = false,
      pathIfNotLoggedIn = '/a/sensible/default',
      // ...
    }) // ...
    
    export default privateRoute({ 
      requireLoggedIn: true, 
      pathIfNotLoggedIn: '/a/specific/page'
    })(MyRoute)
    
    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" />} />
    
        <Query query={fetchUserInfoQuery(moreUserInfo)}>
          {({ loading, error, data: userInfo = {} }: any) => {
            const isNotAuthenticated = !loading && (isEmpty(userInfo) || !userInfo.whoAmI);
    
            if (isNotAuthenticated || error) {
              return <Redirect to={RoutesPaths.Login} />;
            }
            const { whoAmI } = userInfo;
    
            return <Component user={whoAmI} {...renderProps} />;
          }}
        </Query>
    
    const isEmpty = (object: any) => object && Object.keys(object).length === 0