使用Material UI Grid组件时出现Invalid hook call及useContext报错
问题场景
React项目中编写JoinChat组件时,引入Material UI的Grid组件包裹页面内容,页面渲染失败。组件完整代码如下:
import React, { useEffect } from "react"; import { useStore } from "../zustand/store"; import shallow from "zustand/shallow"; import Grid from "@mui/material/Grid"; export const JoinChat = () => { const [userCount, setUserCount] = useStore( (state) => [state.userCount, state.setUserCount], shallow ); const setChat = useStore((state) => state.setChat); const socket = useStore((state) => state.socket); useEffect(() => { socket.on("user_count", (data) => { console.log("user count run in join chat"); setUserCount(data); }); }, [socket, setUserCount]); const joinChat = async () => { if ("geolocation" in navigator) { await navigator.geolocation.getCurrentPosition(async (pos) => { const location = { x: pos.coords.longitude, y: pos.coords.latitude, }; await socket.emit("create_chat", location); setChat(true); }); } else { alert("You location is not available"); } }; return ( <Grid> <div> {userCount ? <p>People online {userCount}</p> : null} <button onClick={joinChat}>Join chat</button> </div> </Grid> ); };
控制台抛出两类报错:
- 警告:
Invalid hook call - 类型错误:
cannot read properties of null (reading 'useContext')
移除所有Material UI组件后,页面可正常渲染,无任何警告或错误。
根因分析
该问题由项目依赖树中存在多份React实例,或React与MUI版本不兼容导致:
- MUI组件内部会调用
useContext等React内置Hook,按照React的Hook规则,Hook必须在对应React实例的组件渲染上下文中调用。如果MUI依赖引用的React,和业务代码运行时加载的React不是同一个实例,MUI内部调用Hook时就找不到对应的上下文,直接抛出上述两类错误。 - 移除MUI组件后报错消失,是因为业务代码自身调用Hook时使用的是当前项目加载的React实例,不存在跨实例调用Hook的问题。
常见触发场景:
- MUI版本与项目React版本不兼容,例如使用MUI v5时React版本低于17.0.0
- 依赖安装异常,导致MUI所在的依赖层级下单独安装了一份独立的React副本
- 本地调试(npm link/monorepo场景)时,主项目和依赖包各自加载了不同路径下的React
修复步骤
按优先级依次操作即可解决:
- 对齐版本要求
MUI v5要求React版本不低于17.0.0,先升级React到兼容版本:npm install react@^18.2.0 react-dom@^18.2.0 - 清理依赖缓存重新安装
这是解决多实例问题最常用的手段,删除现有依赖目录和锁文件后重新安装,保证依赖树中仅存在一份React实例:# npm 执行以下命令 rm -rf node_modules package-lock.json npm install # yarn 执行以下命令 rm -rf node_modules yarn.lock yarn install # pnpm 执行以下命令 rm -rf node_modules pnpm-lock.yaml pnpm install - 构建工具强制指定React解析路径
如果重新安装依赖后问题仍存在,可以在构建配置中添加别名,强制所有模块引用React时都指向项目根目录下的React实例:
Webpack配置示例:
Vite配置示例:// webpack.config.js const path = require('path') module.exports = { // 其余配置省略 resolve: { alias: { react: path.resolve(__dirname, 'node_modules/react'), 'react-dom': path.resolve(__dirname, 'node_modules/react-dom') } } }// vite.config.js import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import path from 'path' export default defineConfig({ plugins: [react()], resolve: { alias: { react: path.resolve(__dirname, './node_modules/react'), 'react-dom': path.resolve(__dirname, './node_modules/react-dom') } } }) - 校验重复依赖
执行包管理命令检查依赖树中是否存在多份React副本,确认修复结果:
输出结果中仅存在根目录下的一份React版本时,即修复完成。# npm npm ls react # yarn yarn list react
内容的提问来源于stack exchange,提问作者reemwn
相关产品推荐
相关产品推荐

