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

Next.js13 APP目录下Mobx Store注水报错:需Hooks支持如何解决?

问题分析与解决方案

当前代码存在的问题

  1. 自定义Hook不符合规则:useStore没有调用任何React内置Hook,React无法识别它为合法Hook,导致mobx-react-lite检测不到Hook支持环境,抛出报错。
  2. Store初始化未绑定React状态:当前initStore的客户端store缓存逻辑未结合React组件生命周期,可能导致组件重渲染时store实例出现异常。
  3. 数据类型不匹配:请求https://jsonplaceholder.typicode.com/todos/1返回单个对象,却被断言为Book[]数组,会导致后续hydrate方法接收错误格式的数据。

修复方案

1. 修正useStore为合法自定义Hook

修改./book-store.ts,加入useMemo确保符合Hook规则,同时保证store实例稳定性:

// we need to enable static rendering for prevent rerender on server side and leaking memory
import { enableStaticRendering } from "mobx-react-lite";
import { BooksStore, type Book } from "./model"
import { useMemo } from "react";

// enable static rendering ONLY on server
enableStaticRendering(typeof window === "undefined")

// init a client store that we will send to client (one store for client)
let clientStore: BooksStore

const initStore = (initData: Book[]) => {
  // check if we already declare store (client Store), otherwise create one
  const store = clientStore ?? new BooksStore();
  // hydrate to store if receive initial data
  if (initData) store.hydrate(initData)

  // Create a store on every server request
  if (typeof window === "undefined") return store
  // Otherwise it's client, remember this store and return 
  if (!clientStore) clientStore = store;
  return store
}

// Hook for using store
export function useStore(initData: Book[]) {
  // 使用useMemo确保store仅在initData变化时重新初始化
  return useMemo(() => initStore(initData), [initData])
}

2. 修正Template组件的数据处理

调整请求接口获取数组格式数据,匹配Book[]类型:

import { useStore } from "@/src/entities/book-store"
import { Store } from "@/src/entities/book-store/ui"
import { Book } from "@/src/entities/book-store/model"
import axios from "axios"
import { use } from "react"

export default function Template({ children }: { children: React.ReactNode }) {
  // 请求数组格式的todos数据
  const data = use(axios({
    url: 'https://jsonplaceholder.typicode.com/todos'
  }).then(data => data.data)) as Book[]    
  const store = useStore(data)
  return <Store.Provider value={store} >{children}</Store.Provider>
}

3. 替代的Store注水方式

如果上述修改仍有问题,可采用服务端预渲染注入全局变量的方式:

  • 服务端渲染时,将初始数据挂载到全局变量:
// 服务端入口示例
const initialData = await axios.get('https://jsonplaceholder.typicode.com/todos').then(res => res.data);
const html = ReactDOMServer.renderToString(`
  <script>window.__INITIAL_STATE__ = ${JSON.stringify(initialData)}</script>
  ${ReactDOMServer.renderToString(<Template>{children}</Template>)}
`);
  • 修改useStore从全局变量读取初始数据:
export function useStore() {
  const initData = typeof window !== "undefined" ? (window as any).__INITIAL_STATE__ : [];
  return useMemo(() => initStore(initData), []);
}
  • Template组件简化为:
export default function Template({ children }: { children: React.ReactNode }) {
  const store = useStore()
  return <Store.Provider value={store} >{children}</Store.Provider>
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 14:53:11