You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 05:07:45