React组件中useSWR封装引发Invalid Hook Call错误排查求助
React中使用自定义SWR Hook触发Invalid hook call错误排查
错误现象
用SWR封装了自定义Hook useSharedState,在Player组件中使用时触发Uncaught Error: Invalid hook call错误。此前使用React Context无异常,重构为SWR+React Router v6后出现该问题。已查阅React官方Hook规则及错误排查文档,仍未定位问题。错误栈显示问题出在ReactNetflixPlayer组件调用useRef时,可能原因包括:
- React与渲染器版本不匹配
- 违反Hook使用规则
- 项目中存在多份React副本
错误详情
react-dom.development.js:18687 The above error occurred in one of your React components: ... line 115341 line 115342 e.default = function (n) { line 115343 var e = n.title, line 115344 t = void 0 !== e && e, line 115345 a = n.subTitle, ... at e.default (http://localhost:3000/static/js/bundle.js:115343:15) at div at Player (http://localhost:3000/static/js/bundle.js:8314:108) at div at div at Tab (http://localhost:3000/static/js/bundle.js:6132:104) at Routes (http://localhost:3000/static/js/bundle.js:122353:5) at Router (http://localhost:3000/static/js/bundle.js:122286:15) at BrowserRouter (http://localhost:3000/static/js/bundle.js:121095:5) at SWRConfig$1 (http://localhost:3000/static/js/bundle.js:137530:21) at App (http://localhost:3000/static/js/bundle.js:2528:86) 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. react-dom.development.js:12056 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 resolveDispatcher (react.development.js:1476:1) at useRef (react.development.js:1515:1) at e.default (ReactNetflixPlayer.js:1:1) at renderWithHooks (react-dom.development.js:16305:1) at mountIndeterminateComponent (react-dom.development.js:20074:1) at beginWork (react-dom.development.js:21587:1) at beginWork$1 (react-dom.development.js:27426:1) at performUnitOfWork (react-dom.development.js:26557:1) at workLoopSync (react-dom.development.js:26466:1) at renderRootSync (react-dom.development.js:26434:1)
依赖配置
"dependencies": { "react": "^18.2.0", "react-dom": "^18.2.0", "react-netflix-player": "^1.1.7", "react-redux": "^8.0.2", "react-router-dom": "^6.3.0", "react-scripts": "^5.0.1", "redux": "^4.2.0", "swr": "^1.3.0" },
相关代码
useSharedState.js
import useSWR from 'swr' export const useSharedState = (key, initial) => { const { data: state, mutate: setState } = useSWR(key, { fallbackData: initial, }) return [state, setState] }
Container.js
import { useSharedState } from "../../hooks/useSharedState" import useSWR from 'swr' function Container() { const { data: item } = useSWR(`/endpoint`) // 注:此处缺少useSharedState的调用,setPlayerItem未定义,需补充 // const [playerItem, setPlayerItem] = useSharedState('playerContext') return ( item && ( <div> <button onClick={() => {setPlayerItem(item) }} ... > ... </button> </div> ) ); } export default Container;
Player.js
import React from 'react'; import ReactNetflixPlayer from "react-netflix-player"; import { useSharedState } from "../../hooks/useSharedState" // 注:原代码未引入该Hook,需补充 const Player = () => { const [playerItem, setPlayerItem] = useSharedState('playerContext') return ( <React.Fragment> {playerItem && ( <div id="player" > <ReactNetflixPlayer title={playerItem.title} backButton={() => { setPlayerItem(null) }} /> </div> )} </React.Fragment> ); } export default Player;
Tab.js
import Player from "../Player" ... function Tab() { return ( <div> <Player /> <React.Fragment> <Container /> {columnData && <BodyColumn data={columnData}/>} ... </React.Fragment> </div> ); } export default Tab;
排查与解决方案
1. 检查第三方组件兼容性
错误栈指向react-netflix-player内部的useRef调用,该组件版本1.1.7发布时间较早,可能未适配React 18:
- 尝试降级React到17.x版本:
npm install react@^17.0.2 react-dom@^17.0.2 - 替换为兼容React 18的播放器组件,如
react-player等。
2. 解决多份React副本问题
项目中可能存在多份React实例,导致Hook调用异常:
- 执行依赖去重:
npm dedupe - 若使用Create React App,可通过
react-app-rewired修改webpack配置,强制所有依赖使用项目根目录的React:
在项目根目录创建config-overrides.js:
再修改const path = require('path'); module.exports = function override(config, env) { config.resolve.alias = { ...config.resolve.alias, 'react': path.resolve(__dirname, 'node_modules/react'), 'react-dom': path.resolve(__dirname, 'node_modules/react-dom') }; return config; }package.json的启动脚本为react-app-rewired start。
3. 修正代码中的Hook使用问题
- Container组件中缺少
useSharedState的调用,导致setPlayerItem未定义,需补充引入并调用该Hook; - Player组件原代码未引入
useSharedState,需补充import语句。
4. 验证Hook调用规则
确保所有Hook调用都在函数组件或自定义Hook的顶层,无嵌套在条件判断、循环或嵌套函数中。现有代码中Player组件的Hook调用符合规则,无需调整。
内容的提问来源于stack exchange,提问作者Ridhwaan Shakeel
相关产品推荐
相关产品推荐

