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

React+Apollo中执行GQL删除Mutation后调用导航Hook触发无效Hook调用错误的问题排查

React+Apollo中执行GQL删除Mutation后调用导航Hook触发无效Hook调用错误的问题排查

我来帮你定位并解决这个问题——你遇到的「Invalid hook call」错误,核心原因是违反了React Hook的调用规则,同时对React Router的导航逻辑使用有误,具体分析和修复方案如下:

错误的核心原因

你在useMutation的onCompleted回调里写的(Navigate("/decks"))存在两个关键问题:

  1. 如果你是把React Router v6的Navigate组件当作普通函数调用,这是完全错误的——组件只能在JSX渲染树中使用,不能在普通回调函数里直接执行。
  2. 如果你原本想在回调里调用useNavigate Hook(比如const navigate = useNavigate(); navigate("/decks")),那也会触发Hook调用错误,因为React Hook只能在函数组件的顶层作用域调用,不能在嵌套的回调、循环或条件判断里使用,而onCompleted属于嵌套回调,显然违反了这个规则。

正确的修复方案

我们需要先在组件顶层通过useNavigate Hook获取导航函数,再在onCompleted回调里调用这个函数来实现页面跳转,具体步骤如下:

1. 导入并在组件顶层获取导航函数

在DeckDeleteModal组件的最顶层(所有Hook和JSX之前),导入useNavigate并调用它拿到导航函数:

import { useNavigate } from 'react-router-dom';

function DeckDeleteModal({showModal, setShowModal, deckId}) {
  // 必须在组件顶层调用useNavigate,不能放在回调里
  const navigate = useNavigate();
  const [error, setError] = useState(null);
  // ... 其他代码
}

2. 修改onCompleted回调实现导航

在useMutation的配置里,调用已获取的navigate函数完成跳转,同时建议先关闭模态框,提升用户体验:

const [deleteDeck, { loading }] = useMutation(DELETE_DECK, {
  onCompleted() {
    setShowModal(false); // 先关闭模态框,避免导航后模态框残留
    navigate("/decks"); // 执行页面跳转
  },
  onError: (error) => {
    setError(error.graphQLErrors[0].message)
  }
});

3. 修复额外的小问题

你的Alert组件原本渲染的是字符串"error",应该渲染错误变量error,否则用户看不到具体的错误信息:

if (error) {
  return <Alert variant="danger">{error}</Alert>;
}

完整的修复后代码

import { useState } from 'react';
import { useMutation } from '@apollo/client';
import { useNavigate } from 'react-router-dom';
import { Modal, Button, Alert, Spinner } from 'react-bootstrap';

function DeckDeleteModal({showModal, setShowModal, deckId}) {
  const navigate = useNavigate();
  const [error, setError] = useState(null);

  const [deleteDeck, { loading }] = useMutation(DELETE_DECK, {
    onCompleted() {
      setShowModal(false);
      navigate("/decks");
    },
    onError: (error) => {
      setError(error.graphQLErrors[0].message)
    }
  });

  if (error) {
    return <Alert variant="danger">{error}</Alert>;
  }

  return (
    <Modal size="sm" variant="dark" centered show={showModal}>
      <Modal.Header>
        <Modal.Title id="contained-modal-title-vcenter">
          Delete deck?
        </Modal.Title>
      </Modal.Header>
      <Modal.Body>
        Are you sure you want to delete this deck?
      </Modal.Body>
      <Modal.Footer>
        <Button 
          variant="danger" 
          disabled={loading} 
          onClick={() => deleteDeck({ variables: { id: deckId} })}
        >
          { loading ? <Spinner size="sm" /> : <>Delete</>}
        </Button>
        <Button onClick={() => setShowModal(false)} variant="light">Close</Button>
      </Modal.Footer>
    </Modal>
  );
}

export default DeckDeleteModal;

额外注意事项

确保你的DeckDeleteModal组件被包裹在React Router的路由上下文(比如BrowserRouter)之内,否则useNavigate会无法获取路由上下文,导致新的错误。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 09:00:30