React中withAuth高阶组件结合Browser Router的类型错误及后续问题
解决React withAuth高阶组件的路由验证与GraphQL查询问题
看起来你在React身份验证的高阶组件(HOC)上遇到了两个关键问题:函数式HOC的调用错误,以及类组件HOC中无法使用Hooks导致的查询失败。让我们一步步拆解并解决这些问题:
一、原函数式HOC的错误原因分析
你最初遇到的TypeError: Object(...) is not a function报错,核心原因是错误调用了GraphQL查询:
getMeQuery应该是一个GraphQL查询文档(字符串或gql解析后的对象),而非可直接执行的函数。你需要用对应的React Hooks(比如Apollo Client的useQuery)来执行它,而不是直接写getMeQuery()。- 另外,虽然函数式HOC的结构本身合规,但路由重定向的逻辑可以更严谨。
二、修正后的函数式withAuth HOC(推荐方案)
函数式HOC结合React Hooks是更现代、简洁的写法,我们调整查询方式并优化验证逻辑:
import React, { useContext } from 'react' import { Redirect } from 'react-router-dom' import { useQuery } from '@apollo/client' // 按你的GraphQL客户端替换,比如urql的useQuery import client from '../client' import { getMeQuery } from '../graphql/queries' import { useCacheService } from './cache.service' const MyContext = React.createContext(null) export const useMe = () => { return useContext(MyContext) } export const withAuth = (Component) => (props) => { // 第一步:检查cookie中的登录状态 const isLoggedIn = isSignedIn() // 未登录时直接重定向到登录页,避免循环重定向 if (!isLoggedIn) { if (props.history.location.pathname === '/sign-in') { return null } return <Redirect to="/sign-in" /> } // 第二步:用useQuery执行GraphQL用户信息查询(关键修正) const { data, error, loading } = useQuery(getMeQuery) // 自定义钩子仅能在函数组件/自定义钩子中使用,此处合法 useCacheService() // 查询加载中:返回加载提示或骨架屏 if (loading) return <div>正在验证身份...</div> // 查询出错或无用户数据:登出并跳回登录页 if (error || !data?.me) { signOut() return <Redirect to="/sign-in" /> } // 验证通过:传递用户上下文并渲染目标组件 return ( <MyContext.Provider value={data.me}> <Component {...props} /> </MyContext.Provider> ) } export const isSignedIn = () => { return document.cookie.includes('authToken') } // 补充登出逻辑(确保清除登录状态) export const signOut = () => { document.cookie = 'authToken=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;' }
三、类组件HOC的问题说明
你改成类组件后遇到的componentDidMount未执行、data.me为null的问题,根源在于:
- 类组件禁止使用React Hooks:
useCacheService和useQuery都是Hooks,只能在函数组件或自定义钩子中使用,类组件中调用它们会直接失效甚至触发报错。 - 生命周期方法不能返回JSX:
componentDidMount仅能执行逻辑操作,不能返回组件,你在其中写的return <Redirect />完全不会生效,重定向逻辑必须移到render方法中处理。
如果一定要用类组件实现,你需要通过GraphQL客户端的直接查询方法(比如Apollo的client.query)获取数据,但这种写法远不如函数式HOC简洁,因此更推荐上面的函数式方案。
四、App.js代码验证
你的App.js代码本身无问题,可补充小细节确保逻辑完整:
import React from 'react'; import { BrowserRouter, Route, Redirect } from 'react-router-dom' import SignInScreen from './components/SignInScreen' import DepartmentScreen from './components/DepartmentScreen' import { withAuth } from './services/auth.service' // 提前定义重定向函数,避免组件内部嵌套 const redirectToDept = () => ( <Redirect to="/dept" /> ) const App = () => ( <BrowserRouter> <Route exact path="/sign-in" component={SignInScreen}/> <Route exact path="/dept" component={withAuth(DepartmentScreen)}/> <Route exact path="/" render={redirectToDept} /> </BrowserRouter> ) export default App;
内容的提问来源于stack exchange,提问作者thisis-Shitanshu
相关产品推荐
相关产品推荐

