Astro+Preact项目使用Material UI报Cannot read 'context'错误求助
Cannot read properties of undefined (reading 'context')错误 问题场景
在基于Preact + TypeScript的Astro项目中引入Material UI的Typography组件时,触发以下错误:
TypeError: Cannot read properties of undefined (reading 'context') at exports.useContext at useTheme (/node_modules/@mui/system/useThemeWithoutDefault.js:15:30) at useTheme (/node_modules/@mui/system/useTheme.js:13:46) at Box (/node_modules/@mui/system/createBox.js:30:41) at Object.t (/node_modules/preact/compat/dist/compat.js:1:1040) at file:///node_modules/preact-render-to-string/dist/index.mjs:1:6332 at F (file:///node_modules/preact-render-to-string/dist/index.mjs:1:6359) at F (file:///node_modules/preact-render-to-string/dist/index.mjs:1:6434) at Proxy.S (file:///node_modules/preact-render-to-string/dist/index.mjs:1:4833) at Object.renderToStaticMarkup (/node_modules/@astrojs/preact/dist/server.js:55:45) at renderFrameworkComponent (/node_modules/astro/dist/runtime/server/render/component.js:187:66)
仅当给组件添加client:only="preact"属性时能正常运行,但希望避免每次使用MUI组件都手动添加该配置。
已尝试的方案
- 在Astro Preact集成配置中设置
preact({ compat: true }) - 在
package.json中通过dependencies和overrides将react、react-dom替换为npm:@preact/compat@latest - 查阅Material UI官方Preact示例仓库、搜索相关issue(含Vitest相关问题),尝试修改
preact/hooks配置和dev命令,均无效
原因分析
这个错误的核心是服务端渲染(SSR)阶段,Material UI依赖的ThemeContext未被正确初始化。虽然preact/compat模拟了React的Context API,但在Astro的SSR流程中,服务端无法正确注入MUI的ThemeProvider上下文,导致useContext调用时获取不到ThemeContext实例,最终抛出错误。
而client:only="preact"让组件跳过SSR,仅在客户端渲染,此时客户端的Context已被正常初始化,因此不会触发错误。
可行解决办法
1. 全局包裹ThemeProvider并强制客户端初始化
在Astro根布局中用Preact组件全局包裹应用,确保ThemeContext在客户端提前激活:
- 创建Preact主题包装组件
src/components/ThemeWrapper.tsx:
import { ThemeProvider, createTheme } from '@mui/material/styles'; import CssBaseline from '@mui/material/CssBaseline'; // 可根据需求自定义主题 const defaultTheme = createTheme(); export default function ThemeWrapper({ children }) { return ( <ThemeProvider theme={defaultTheme}> <CssBaseline /> {children} </ThemeProvider> ); }
- 在根布局文件(如
src/layouts/Layout.astro)中使用该组件,并添加client:load指令:
--- import ThemeWrapper from '../components/ThemeWrapper'; --- <html lang="en"> <body> <ThemeWrapper client:load> <slot /> </ThemeWrapper> </body> </html>
该方案让ThemeContext在客户端初始化完成,部分MUI组件可能可以正常进行SSR渲染,但仍存在部分组件依赖客户端Context的情况,需测试验证。
2. 封装MUI组件统一添加客户端渲染指令
创建封装组件,自动为MUI组件添加client:only="preact",避免重复手动配置:
以Typography为例,创建src/components/MuiTypography.astro:
--- import Typography from '@mui/material/Typography'; --- <Typography client:only="preact" {...Astro.props} />
之后在项目中直接导入这个封装组件使用即可:
--- import MuiTypography from '../components/MuiTypography.astro'; --- <MuiTypography variant="h1">Hello Material UI</MuiTypography>
同理可封装其他MUI组件,统一管理客户端渲染指令。
3. 关于版本兼容性的说明
目前Material UI v5对Preact的兼容主要集中在客户端场景,SSR下preact/compat与MUI的Context系统存在适配局限,属于MUI v5与Preact SSR的硬性兼容问题。官方提到MUI v6会更好地支持Preact,但在v6正式发布前,SSR场景下无法完全规避客户端渲染指令的使用。
建议确保使用最新版本的Astro Preact集成和preact/compat,新版本可能修复了部分兼容性问题。
内容的提问来源于stack exchange,提问作者Tony Kovar

