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

MobX-State-Tree存储Keycloak实例失效问题求助

问题分析与修复方案:Keycloak实例在MobX-State-Tree中仅能正常工作一次的问题

问题根源

你遇到的核心问题在于把有状态的Keycloak实例塞进了MST的可序列化状态模型里。MST的状态设计初衷是存储可序列化的纯数据,而你用types.custom定义的KeycloakPrimitive在处理状态更新时,会触发快照的序列化/反序列化逻辑——每次MST需要恢复状态时,都会调用fromSnapshot创建一个全新的、未初始化的Keycloak实例,这个新实例还没执行init(),自然没有authServerUrl这类初始化后才会填充的属性。

另外你在getProfileData里写的self.keycloakInstance = self.keycloakInstance完全是画蛇添足,这行代码会触发MST的状态校验逻辑,进一步导致实例被意外替换。

正确的解决方案

我们需要把Keycloak实例从MST的可序列化状态中剥离,改用volatile属性存储——这类属性不参与MST的快照机制,专门用来存放像Keycloak这种带内部状态、不可序列化的实例对象。

修改后的代码示例

auth.store.ts

import { types } from 'mobx-state-tree'
import { KeycloakInstance } from 'keycloak-js'
import Keycloak from 'keycloak-js'

const AuthStore = types.model('AuthStore', {
  // 只放可序列化的纯数据,比如用户profile
  userProfile: types.maybeNull(types.frozen())
})
// 定义volatile属性,存储Keycloak实例(不参与快照)
.volatile(self => ({
  keycloakInstance: null as KeycloakInstance | null
}))
.actions(self => ({
  // 用于外部传入Keycloak实例
  setKeycloakInstance(kc: KeycloakInstance) {
    self.keycloakInstance = kc
  },
  test(value: any) {
    console.log(value)
  },
  init() {
    if (!self.keycloakInstance) return
    self.keycloakInstance.init({ onLoad: 'login-required' })
      .success((value) => {
        self.test(value)
        // 初始化成功后再拉取用户信息,避免异步时序问题
        self.getProfileData()
      })
      .error(err => console.error('Keycloak初始化失败:', err))
  },
  getProfileData() {
    if (!self.keycloakInstance) return
    self.keycloakInstance.loadUserProfile()
      .success(profile => {
        self.userProfile = profile
        console.log(profile)
      })
      .error(err => console.error('拉取用户信息失败:', err))
  }
}))

export { AuthStore }

index.ts(初始化store的文件)

import { Store } from './Store'
import Keycloak from 'keycloak-js'

// 先初始化Keycloak实例
const kc = new Keycloak({
  // 你的Keycloak配置JSON
})

// 创建store时只传入可序列化的状态数据
const store = Store.create({
  drawerStore: { isOpen: false },
  appBarStore: { isUserMenuOpen: false },
  authStore: { userProfile: null }
})

// 把Keycloak实例设置到authStore的volatile属性中
store.authStore.setKeycloakInstance(kc)

export default store

home.ts(使用store的文件)

import React from 'react'
import './Home.css'
import Page from '../../components/Page'
import { inject, observer } from 'mobx-react'
import { InjectProps } from '../../utils/interfaces/InjectProps'

@inject('store')
@observer
class Home extends React.Component {
  get injected(): InjectProps {
    return this.props as InjectProps
  }

  componentDidMount() {
    const { authStore } = this.injected.store
    // 只在组件挂载时初始化一次
    authStore.init()
  }

  render() {
    const { authStore } = this.injected.store
    return (
      <>
        <Page>
          <h1>Hello you are logged in!</h1>
          {authStore.userProfile && (
            <p>Welcome, {authStore.userProfile.username}</p>
          )}
        </Page>
      </>
    )
  }
}

export { Home }

额外重要提醒

  1. 异步操作时序:Keycloak的init是异步的,绝对不要在render方法里调用getProfileData——这会导致重复请求甚至无限渲染循环,一定要在init的成功回调里触发后续操作。
  2. 避免状态污染:永远不要把带内部状态的实例(比如SDK实例、DOM元素)放进MST的可序列化模型里,这类对象应该用volatile属性或者store的私有属性存储。

内容的提问来源于stack exchange,提问作者Marco Ferraioli

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:58:10