You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Flow应用报错:Invalid hook call 问题排查与解决求助

React Flow 应用出现 Invalid hook call 错误的原因及解决方法

错误现象

应用抛出错误:Invalid hook call. Hooks can only be called inside of the body of a function component,即使简化LoadData函数后仍报错。

原代码示例

LoadData 函数

function LoadData() {
  const [graphData, setJsonResults] = useState([]);

  useEffect(() => {
    async function fetchData() {
      const data = await GetData();
      setJsonResults(data);
    }
    fetchData();
  }, []);

  return (
    <div>
      {graphData.map((item, index) => (
        <p key={index}>{item}</p>
      ))}
    </div>
  );
}

App 组件

export default function App() {
  const [nodes, setNodes] = useState([]);
  const [edges, setEdges] = useState([]);

  return (
    <div style={{ width: '100vw', height: '100vh' }}>
      <button className="MyNewButton" onClick={LoadData}>Load Data</button>
      <ReactFlow
        nodes={nodes}
        edges={edges}
        onNodesChange={setNodes}
        onEdgesChange={setEdges}
      >
        <Controls />
        <MiniMap />
        <Background variant="dots" gap={12} size={1} />
      </ReactFlow>
    </div>
  );
}

修改后仍报错的 LoadData 函数

const LoadData =()=>{
  const [graphData, setJsonResults] = useState([]);

  return (
    <div>
      Happy Days are here again
    </div>
  )  
}

依赖版本信息

package.json

{
  "name": "my-react-flow-app",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "antd": "^5.3.3",
    "neo4j-driver": "^5.6.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "reactflow": "^11.7.0"
  },
  "devDependencies": {
    "@types/react": "^18.0.28",
    "@types/react-dom": "^18.0.11",
    "@vitejs/plugin-react": "^3.1.0",
    "vite": "^4.2.0"
  }
}

npm ls react-dom 结果

npm ls react-dom
├─┬ antd@5.3.3
│ ├─┬ @ant-design/cssinjs@1.7.1
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ @ant-design/icons@5.0.1
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ @rc-component/mutate-observer@1.0.0
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ @rc-component/tour@1.8.0
│ │ ├─┬ @rc-component/portal@1.1.1
│ │ │ └── react-dom@18.2.0 deduped
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ @rc-component/trigger@1.6.2
│ │ ├─┬ rc-align@4.0.15
│ │ │ └── react-dom@18.2.0 deduped
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-cascader@3.9.1
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-checkbox@2.3.2
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-collapse@3.5.2
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-dialog@9.0.2
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-drawer@6.1.5
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-dropdown@4.0.1
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-field-form@1.28.0
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-image@5.15.2
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-input-number@7.4.2
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-input@0.2.2
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-mentions@2.1.0
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-menu@9.8.3
│ │ ├─┬ rc-overflow@1.3.0
│ │ │ └── react-dom@18.2.0 deduped
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-motion@2.6.3
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-notification@5.0.3
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-pagination@3.3.1
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-picker@3.3.4
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-progress@3.4.1
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-rate@2.10.0
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-resize-observer@1.3.1
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-segmented@2.1.2
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-select@14.3.0
│ │ ├─┬ rc-virtual-list@3.4.13
│ │ │ └── react-dom@18.2.0 deduped
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-slider@10.1.1
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-steps@6.0.0
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-switch@4.0.0
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-table@7.31.1
│ │ ├─┬ @rc-component/context@1.3.0
│ │ │ └── react-dom@18.2.0 deduped
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-tabs@12.5.10
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-textarea@1.1.0
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-tooltip@6.0.1
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-tree-select@5.7.0
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-tree@5.7.3
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-trigger@5.3.4
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-upload@4.3.4
│ │ └── react-dom@18.2.0 deduped
│ ├─┬ rc-util@5.29.3
│ │ └── react-dom@18.2.0 deduped
│ └── react-dom@18.2.0 deduped
├── react-dom@18.2.0
└─┬ reactflow@11.7.0
  ├─┬ @reactflow/background@11.2.0
  │ └── react-dom@18.2.0 deduped
  ├─┬ @reactflow/controls@11.1.11
  │ └── react-dom@18.2.0 deduped
  ├─┬ @reactflow/core@11.7.0
  │ └── react-dom@18.2.0 deduped
  ├─┬ @reactflow/minimap@11.5.0
  │ └── react-dom@18.2.0 deduped
  ├─┬ @reactflow/node-resizer@2.1.0
  │ └── react-dom@18.2.0 deduped
  ├─┬ @reactflow/node-toolbar@1.1.11
  │ └── react-dom@18.2.0 deduped
  └── react-dom@18.2.0 deduped

错误原因

你把函数组件LoadData当成普通事件处理函数直接在onClick中调用了。React组件必须通过JSX语法(如<LoadData />)渲染,这样React才能在组件渲染周期内管理其中的Hooks。直接调用LoadData()相当于执行普通函数,里面的useState和useEffect不在React组件的渲染上下文中执行,违反了Hooks的使用规则,因此抛出错误。

解决方案

根据需求分两种情况处理:

情况1:点击按钮后显示LoadData的渲染内容

在App组件中添加状态控制LoadData的显示,将其作为组件渲染而非事件处理函数:

export default function App() {
  const [nodes, setNodes] = useState([]);
  const [edges, setEdges] = useState([]);
  const [showData, setShowData] = useState(false); // 新增状态控制显示

  return (
    <div style={{ width: '100vw', height: '100vh' }}>
      <button className="MyNewButton" onClick={() => setShowData(true)}>Load Data</button>
      {/* 条件渲染LoadData组件 */}
      {showData && <LoadData />}
      <ReactFlow
        nodes={nodes}
        edges={edges}
        onNodesChange={setNodes}
        onEdgesChange={setEdges}
      >
        <Controls />
        <MiniMap />
        <Background variant="dots" gap={12} size={1} />
      </ReactFlow>
    </div>
  );
}

情况2:点击按钮后加载数据并更新React Flow的节点/边

如果目标是加载数据后更新React Flow的nodes和edges,无需渲染额外内容,直接将数据加载逻辑移到App组件的事件处理函数中:

export default function App() {
  const [nodes, setNodes] = useState([]);
  const [edges, setEdges] = useState([]);

  // 直接在App内定义事件处理函数
  const handleLoadData = async () => {
    const data = await GetData();
    // 根据实际数据格式转换为React Flow所需的节点/边结构
    setNodes(data.nodes || []);
    setEdges(data.edges || []);
  };

  return (
    <div style={{ width: '100vw', height: '100vh' }}>
      <button className="MyNewButton" onClick={handleLoadData}>Load Data</button>
      <ReactFlow
        nodes={nodes}
        edges={edges}
        onNodesChange={setNodes}
        onEdgesChange={setEdges}
      >
        <Controls />
        <MiniMap />
        <Background variant="dots" gap={12} size={1} />
      </ReactFlow>
    </div>
  );
}

内容的提问来源于stack exchange,提问作者spacedog

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 09:34:52