Next.js中Mobx Observer变更未触发重渲染问题排查
问题排查与解决思路
首先明确:Mobx 和 Next.js 不存在兼容性问题,你的问题大概率是 Mobx 的使用方式有误,以下是最常见的几个排查点:
1. 组件未用 observer 包裹
Mobx 需要通过 observer 高阶组件标记组件,才能让组件自动响应状态变化。如果 Hero 组件没加这个包裹,哪怕状态更新了,组件也不会重渲染。
示例修正:
// Hero.tsx import { observer } from 'mobx-react-lite' // 用 observer 包裹组件 const Hero = observer(() => { const accountStore = /* 获取你的 Store 实例 */ return <button>{accountStore.address}</button> }) export default Hero
2. Store 状态未标记为可观察
AccountStore 里的 address 字段如果没有被 Mobx 标记为可观察对象,状态更新时不会被追踪,组件自然感知不到变化。
方式一:用 makeAutoObservable 自动标记(推荐)
// AccountStore.ts import { makeAutoObservable } from 'mobx' class AccountStore { address = '' constructor() { // 自动将所有属性标记为 observable,方法标记为 action makeAutoObservable(this) } login() { this.address = 'Hello World' } } // 导出单例实例,确保所有组件共用同一个 Store export const accountStore = new AccountStore()
方式二:用 makeObservable 手动标记
// AccountStore.ts import { makeObservable, observable, action } from 'mobx' class AccountStore { address = '' constructor() { makeObservable(this, { address: observable, // 标记为可观察状态 login: action // 标记为修改状态的动作 }) } login() { this.address = 'Hello World' } } export const accountStore = new AccountStore()
3. Store 实例未共享
如果 Navbar 和 Hero 组件各自创建了新的 AccountStore 实例,两者的状态完全独立,点击 Navbar 的按钮自然不会影响 Hero 的显示。
必须确保所有组件使用同一个 Store 实例:
- 不要在组件内部
new AccountStore(),而是直接导入上面导出的单例accountStore; - 或者用 React Context 全局注入 Store(适合多 Store 场景),示例如下:
// StoreProvider.tsx import { createContext, useContext, useEffect, useState } from 'react' import { AccountStore } from './AccountStore' const StoreContext = createContext<AccountStore | null>(null) export const StoreProvider = ({ children }: { children: React.ReactNode }) => { const [store, setStore] = useState<AccountStore | null>(null) // 只在客户端初始化 Store,避免 SSR 时重复创建 useEffect(() => { setStore(new AccountStore()) }, []) if (!store) return null return ( <StoreContext.Provider value={store}> {children} </StoreContext.Provider> ) } // 自定义 Hook 方便组件获取 Store export const useStore = () => { const store = useContext(StoreContext) if (!store) throw new Error('useStore 必须在 StoreProvider 内部使用') return store }
然后在 _app.tsx 里全局包裹:
import { StoreProvider } from '../stores/StoreProvider' function MyApp({ Component, pageProps }) { return ( <StoreProvider> <Component {...pageProps} /> </StoreProvider> ) } export default MyApp
组件内通过 useStore() 获取实例:
const Hero = observer(() => { const accountStore = useStore() return <button>{accountStore.address}</button> })
4. 检查状态修改是否在 action 内
虽然 makeAutoObservable 会自动把方法标记为 action,但如果是直接在组件里修改状态(而非调用 Store 的方法),需要用 runInAction 包裹:
// 错误示例:直接修改状态 const Navbar = () => { const accountStore = useStore() return <button onClick={() => accountStore.address = 'Hello World'}>Do Stuff</button> } // 正确示例:用 runInAction 包裹 import { runInAction } from 'mobx' const Navbar = () => { const accountStore = useStore() return ( <button onClick={() => { runInAction(() => { accountStore.address = 'Hello World' }) }}>Do Stuff</button> ) }
优先推荐调用 Store 内定义的 login 方法修改状态,这样更符合 Mobx 的规范。
内容的提问来源于stack exchange,提问作者Ali Gasimzade
相关产品推荐
相关产品推荐

