老胡茶室
老胡茶室

排错:React Invalid hook call

小纪同学

症状

添加了文章图片点击放大功能后,界面交互正常,但无意间发现控制台出现了报错:

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://react.dev/link/invalid-hook-call for tips about how to debug and fix this problem.

提示 Invalid hook call 错误,并且给了相关链接: https://react.dev/link/invalid-hook-call

原因

我们访问 链接 可以看到一个关于 Rules of Hooks 的文档,里面详细说明了控制台报错的 3 种情况:

1. Breaking Rules of Hooks

Breaking Rules of Hooks 1 这些情况违反了 React Hooks 的使用规范,需要检查代码中 Hooks 的使用情况。

2. Mismatching Versions of React and React DOM

Breaking Rules of Hooks 2 使用的 react-dom 或者 react-native 版本太低。

3. Duplicate React

Breaking Rules of Hooks 3 reactreact-dom 导入的模块不一致,可在 index.js 中导入 react 查看:

// Add this in node_modules/react-dom/index.js
window.React1 = require('react');

// Add this in your component file
require('react-dom');
window.React2 = require('react');
console.log(window.React1 === window.React2);

经过排查发现,文章详情页里面引用了 2React 组件,一一屏蔽运行后原来是我新增的 ImageViewer 组件有问题。

将文档和代码提交给 AI 分析后, 发现 handleClick 中更新了多个状态,违反了: Do not call Hooks in event handlers 规范。

 // 问题代码
 const handleClick = (e: MouseEvent) => {
    e.preventDefault();
    e.stopPropagation();

    setIsLoading(true);
    setCurrentImage(img.src);
    setCurrentAlt(img.alt || "");
    setIsOpen(true);
  };

解决

询问 AI 解决方案: Breaking Rules of Hooks 4AI 优化的代码调整到组件中:

  const dispatchRef = useRef(dispatch);

  useEffect(() => {
    dispatchRef.current = dispatch;
  });

 // 其他代码... 
 const handleClick = (e: MouseEvent) => {
    e.preventDefault();
    e.stopPropagation();

    dispatchRef.current({
      type: "OPEN_IMAGE",
      src: img.src,
      alt: img.alt || "",
    });
  };

🎉🎉🎉 刷新页面,这个问题解决了!😄

此外,文档中提到可以用 eslint-plugin-react-hookscatchBreaking Rules of Hooks 规范。我们项目用的是 biomebiome 对于 eslint-plugin-react-hooks 的相关配置configure

 {
  "linter": {
    "rules": {
      "correctness": {
        "useExhaustiveDependencies": "error",
        "useHookAtTopLevel": "error"
      }
    }
  }
}

但是经过测试,发现对 Do not call Hooks in event handlers 规范无效,对其他规范是有效的:

Breaking Rules of Hooks 6

Breaking Rules of Hooks 5

总结

  • 注意 React Hooks 的使用规范
  • 利用 AI 辅助分析问题