React中useState Hook无效调用错误的解决方案咨询
React useState Hook触发Invalid hook call错误的排查与解决
错误信息
react-dom.development.js:16227 Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons: 1. You might have mismatching versions of React and the renderer (such as React DOM) 2. You might be breaking the Rules of Hooks 3. You might have more than one copy of React in the same app See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem. at Object.throwInvalidHookError (react-dom.development.js:16227:1) at useState (react.development.js:1622:1) at MenuWrapper (menuWrapper.jsx:16:1) at MenuList.jsx:12:1 at Array.map (<anonymous>) at MenuList (MenuList.jsx:8:1) at Menu.render (menu.jsx:108:1) at finishClassComponent (react-dom.development.js:19752:1) at updateClassComponent (react-dom.development.js:19698:1) at beginWork (react-dom.development.js:21611:1) react-dom.development.js:18687 The above error occurred in the <Menu> component: at Menu (http://localhost:3000/static/js/bundle.js:385:5) at div at div at App (http://localhost:3000/static/js/bundle.js:62087:78) Consider adding an error boundary to your tree to customize error handling behavior. Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries. logCapturedError @ react-dom.development.js:18687 react-dom.development.js:26923 Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons: 1. You might have mismatching versions of React and the renderer (such as React DOM) 2. You might be breaking the Rules of Hooks 3. You might have more than one copy of React in the same app See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem. at Object.throwInvalidHookError (react-dom.development.js:16227:1) at useState (react.development.js:1622:1) at MenuWrapper (menuWrapper.jsx:16:1) at MenuList.jsx:12:1 at Array.map (<anonymous>) at MenuList (MenuList.jsx:8:1) at Menu.render (menu.jsx:108:1) at finishClassComponent (react-dom.development.js:19752:1) at updateClassComponent (react-dom.development.js:19698:1) at beginWork (react-dom.development.js:21611:1)
package.json配置
{ "name": "jonathan", "version": "0.1.0", "private": true, "type": "module", "dependencies": { "@babel/runtime": "^7.5.0", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^14.4.3", "airbnb-prop-types": "^2.16.0", "axios": "^1.1.3", "bootstrap": "^5.2.2", "create-webpack-config": "^0.2.1", "html-webpack-plugin": "^5.5.0", "jquery": "^3.6.1", "mdb-react-ui-kit": "^4.2.0", "react": "^18.2.0", "react-bootstrap": "^2.5.0", "react-bootstrap-accordion": "^1.0.0", "react-dom": "^18.2.0", "react-helmet": "^6.1.0", "react-onclickout": "^2.0.8", "react-outside-click-handler": "^1.3.0", "react-router": "^6.4.2", "react-router-dom": "^6.4.2", "react-script-tag": "^1.1.2", "react-scripts": "^5.0.1", "type-fest": "^3.1.0", "webpack-dev-server": "^4.11.1" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" }, "eslintConfig": { "extends": [ "react-app", "react-app/jest" ] }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] }, "description": "This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).", "main": "index.js", "keywords": [], "author": "", "license": "ISC", "devDependencies": { "@babel/core": "^7.19.6", "@babel/node": "^7.20.0", "@babel/preset-env": "^7.19.4", "@babel/preset-react": "^7.18.6", "web-vitals": "^3.0.4", "webpack": "^5.74.0", "webpack-cli": "^4.10.0" } }
相关组件代码
MenuWrapper组件(原代码)
import React, { useState }from "react"; import Area from "../ReadFolder/geographicMainComponents/areaMainComponent.jsx"; import City from "../ReadFolder/geographicMainComponents/cityMainComponent.jsx"; import Country from "../ReadFolder/geographicMainComponents/countryMainComponent.jsx"; import Neighborhood from "../ReadFolder/geographicMainComponents/neighborhoodMainComponent.jsx"; import Region from "../ReadFolder/geographicMainComponents/regionMainComponent.jsx"; import { default as AreaW} from "../WriteFolder/geographicMainComponents/areaMainComponent.jsx"; import { default as CityW} from "../WriteFolder/geographicMainComponents/cityMainComponent.jsx"; import { default as CountryW} from "../WriteFolder/geographicMainComponents/countryMainComponent.jsx"; import { default as NeighborhoodW} from "../WriteFolder/geographicMainComponents/neighborhoodMainComponent.jsx"; import { default as RegionW} from "../WriteFolder/geographicMainComponents/regionMainComponent.jsx"; export function MenuWrapper(type,id,isEdit){ let [edit,setEdit] = useState(isEdit); if(isEdit){ if(type==='country'){ return( <div> <div className={edit ? 'd-block' :'d-none'}><CountryW id={id}/><button onclick={() => setEdit=false}>read</button></div> <div className={edit ? 'd-none' : 'd-block'}><Country id={id} /><button onclick={() => setEdit=true}>edit</button></div> </div> ); } else{return(<div></div>)} } else{return(<div></div>)} }
MenuList组件(原代码)
import React,{Component} from "react"; import { MenuWrapper } from "./menuWrapper.jsx"; export function MenuList(Items,kind){ if(Items===null){ return(<div></div>) } return( <div> { Items.map((item)=>{ return( MenuWrapper(kind,item.id,false) ) }) } </div> ); }
已尝试操作
- 删除
package-lock.json和node_modules,执行npm install --force后重启项目,问题未解决。
排查与解决建议
核心问题分析
错误提示明确指向违反了Hook规则:Hooks只能在函数组件的顶层或自定义Hook中调用。你的代码存在两个关键问题:
MenuWrapper不是标准的React函数组件:它直接接收独立参数而非React组件要求的props对象,且你在MenuList中像调用普通函数一样执行MenuWrapper(kind,item.id,false),而非以JSX组件形式<MenuWrapper />渲染,导致useState在非React组件上下文执行。MenuList同样不符合React组件规范,接收参数方式错误,且未以正确方式渲染子组件。
修复步骤
1. 修复MenuWrapper组件
将其改为标准的React函数组件,接收props对象,同时修正事件绑定和状态更新的错误:
import React, { useState }from "react"; // 导入组件部分保持不变 import Area from "../ReadFolder/geographicMainComponents/areaMainComponent.jsx"; import City from "../ReadFolder/geographicMainComponents/cityMainComponent.jsx"; import Country from "../ReadFolder/geographicMainComponents/countryMainComponent.jsx"; import Neighborhood from "../ReadFolder/geographicMainComponents/neighborhoodMainComponent.jsx"; import Region from "../ReadFolder/geographicMainComponents/regionMainComponent.jsx"; import { default as AreaW} from "../WriteFolder/geographicMainComponents/areaMainComponent.jsx"; import { default as CityW} from "../WriteFolder/geographicMainComponents/cityMainComponent.jsx"; import { default as CountryW} from "../WriteFolder/geographicMainComponents/countryMainComponent.jsx"; import { default as NeighborhoodW} from "../WriteFolder/geographicMainComponents/neighborhoodMainComponent.jsx"; import { default as RegionW} from "../WriteFolder/geographicMainComponents/regionMainComponent.jsx"; // 接收props对象,解构出需要的参数 export function MenuWrapper({ type, id, isEdit }) { const [edit, setEdit] = useState(isEdit); // React事件名需用驼峰式onClick,且setEdit是函数需调用而非赋值 if (isEdit) { if (type === 'country') { return ( <div> <div className={edit ? 'd-block' :'d-none'}> <CountryW id={id}/> <button onClick={() => setEdit(false)}>read</button> </div> <div className={edit ? 'd-none' : 'd-block'}> <Country id={id} /> <button onClick={() => setEdit(true)}>edit</button> </div> </div> ); } return <div></div>; } return <div></div>; }
2. 修复MenuList组件
改为标准函数组件,接收props,并以JSX形式渲染MenuWrapper,同时添加列表项必需的key属性:
import React from "react"; import { MenuWrapper } from "./menuWrapper.jsx"; // 接收props对象,解构参数 export function MenuList({ items, kind }) { if (!items) { return <div></div>; } return ( <div> {items.map((item) => ( // 列表项必须添加唯一key属性 <MenuWrapper key={item.id} type={kind} id={item.id} isEdit={false} /> ))} </div> ); }
3. 排除其他潜在问题
- 版本匹配:你的
react和react-dom均为18.2.0,版本一致,排除版本不匹配问题。 - 多React副本:若修复后仍有问题,执行
npm ls react查看是否存在多个React版本,若有则执行npm dedupe合并依赖。
内容的提问来源于stack exchange,提问作者jonathan Daboush
相关产品推荐
相关产品推荐

