Next.js中Mobx结合Next-Auth使用accessToken的问题求助
问题分析
你遇到的错误Error: Invalid hook call是因为违反了React钩子的调用规则:自定义钩子只能在函数组件的顶层或其他自定义钩子的顶层调用,不能在Mobx商店的类方法(比如loadAll)里直接调用useAuthAPI。
解决方案
核心思路是将Next-Auth管理的accessToken从React上下文传递给Mobx商店,而非让商店直接依赖React钩子。以下是具体实现步骤:
1. 改造Axios实例,支持动态传入accessToken
修改setupAuthAPI,让它可以接收accessToken参数,移除对钩子的依赖:
// @/services/authAPI.js import axios from 'axios' export const setupAuthAPI = (accessToken) => { const api = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_URL, // 替换为你的API地址 }) if (accessToken) { api.interceptors.request.use( config => { if (!config.headers.Authorization) { config.headers.Authorization = `Bearer ${accessToken}` } return config }, error => Promise.reject(error) ) } return api }
2. 调整Mobx商店,接收accessToken参数
修改商店的loadAll方法,让它通过参数获取accessToken,不再调用React钩子:
// Mobx商店文件 import { makeAutoObservable } from 'mobx' import { setupAuthAPI } from '@/services/authAPI' import { toast } from 'react-toastify' class ResellerStore implements ResellerStoreInterface { resellerOptions: ResellerOption[] = [] loading = false constructor() { makeAutoObservable(this, {}, { autoBind: true }) } async loadAll(accessToken) { this.loading = true this.resellerOptions = [] try { const api = setupAuthAPI(accessToken) const response = await api.get('Reseller/all') this.resellerOptions = response.data.map(reseller => ({ label: reseller.companyName, value: reseller.id })) } catch (error) { toast.error('加载经销商列表失败') // 可添加自定义错误处理逻辑 } finally { this.loading = false } } } export const resellerStore = new ResellerStore() export { observer }
3. 在React组件中传递accessToken给商店
在使用Mobx商店的函数组件里,通过useSession获取accessToken,再传递给商店的方法:
// 示例组件 import { observer } from 'mobx-react' import { useSession } from 'next-auth/react' import { resellerStore } from '@/stores/resellerStore' import { useEffect } from 'react' const ResellerSelect = () => { const { data: session } = useSession() useEffect(() => { if (session?.accessToken) { resellerStore.loadAll(session.accessToken) } }, [session]) if (resellerStore.loading) { return <div>加载中...</div> } return ( <select> {resellerStore.resellerOptions.map(opt => ( <option key={opt.value} value={opt.value}>{opt.label}</option> ))} </select> ) } export default observer(ResellerSelect)
可选:全局同步Session到Mobx根商店
如果多个组件/子商店都需要用到accessToken,可以在根组件监听Session变化,同步到根商店,避免重复传递:
// _app.js import { SessionProvider } from 'next-auth/react' import { useEffect } from 'react' import { rootStore } from '@/stores/rootStore' function MyApp({ Component, pageProps: { session, ...pageProps } }) { useEffect(() => { if (session?.accessToken) { rootStore.setAccessToken(session.accessToken) } }, [session]) return ( <SessionProvider session={session}> <Component {...pageProps} /> </SessionProvider> ) } export default MyApp
根商店与子商店示例:
// @/stores/rootStore.js import { makeAutoObservable } from 'mobx' class RootStore { accessToken = '' constructor() { makeAutoObservable(this, {}, { autoBind: true }) } setAccessToken(token) { this.accessToken = token } } export const rootStore = new RootStore() // @/stores/resellerStore.js class ResellerStore implements ResellerStoreInterface { rootStore resellerOptions: ResellerOption[] = [] loading = false constructor(rootStore) { this.rootStore = rootStore makeAutoObservable(this, {}, { autoBind: true }) } async loadAll() { if (!this.rootStore.accessToken) return this.loading = true this.resellerOptions = [] try { const api = setupAuthAPI(this.rootStore.accessToken) const response = await api.get('Reseller/all') this.resellerOptions = response.data.map(reseller => ({ label: reseller.companyName, value: reseller.id })) } catch (error) { toast.error('加载经销商列表失败') } finally { this.loading = false } } } export const resellerStore = new ResellerStore(rootStore)
内容的提问来源于stack exchange,提问作者Caio Amorim
相关产品推荐
相关产品推荐

