Reactjs 如何使用Apollo客户端+;根据用户状态对路由器进行响应以实现私有路由和重定向?
我使用React路由器4进行路由,Apollo客户端进行数据获取和缓存。我需要根据以下标准实施PrivateRoute和重定向解决方案: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页面。当用
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