Next.js13 APP目录下Mobx Store注水报错:需Hooks支持如何解决?
问题分析与解决方案
当前代码存在的问题
- 自定义Hook不符合规则:
useStore没有调用任何React内置Hook,React无法识别它为合法Hook,导致mobx-react-lite检测不到Hook支持环境,抛出报错。 - Store初始化未绑定React状态:当前
initStore的客户端store缓存逻辑未结合React组件生命周期,可能导致组件重渲染时store实例出现异常。 - 数据类型不匹配:请求
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
相关产品推荐
相关产品推荐

