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

React自定义钩子报错:downloadFunc不是函数,求问题排查

问题根源分析
  • React Query误用:useQuery的核心作用是自动获取并缓存数据,组件挂载后会立即执行queryFn。但你的场景是点击按钮触发下载,属于手动触发的操作,用useQuery完全不符合设计意图,反而会导致初始化时就执行无参数的downloadWorkInstructions(),返回的函数不具备接收fileId的能力。
  • 函数逻辑错误:downloadWorkInstructions定义时接收fileId,但内部嵌套的downloadFunction并没有使用这个参数,返回的是一个无参函数,后续调用downloadFunc(workInstructionsId)时参数根本无法传递到请求URL中。
  • 数据流转错误:useQuery返回的data是downloadWorkInstructions()的执行结果(即那个无参函数),但组件初始渲染时可能data还未赋值,或者函数本身不支持传参,导致调用时报错。
修复方案

推荐方案:移除React Query,改用手动管理状态的自定义钩子

触发式下载不需要useQuery的自动缓存/请求能力,改用普通工具函数+状态管理更合理:

修复后的自定义钩子

import { useState } from 'react';
import { basePath } from '../../config/basePath';

// 核心下载逻辑
async function downloadWorkInstructions(fileId) {
  const response = await fetch(`${basePath}/files/download-all/${fileId}`, {
    method: 'GET',
  });
  return response;
}

export function useDownloadWorkInstructions() {
  const [isLoading, setIsLoading] = useState(false);

  // 封装带加载状态的下载方法
  const download = async (fileId) => {
    setIsLoading(true);
    try {
      return await downloadWorkInstructions(fileId);
    } finally {
      setIsLoading(false);
    }
  };

  return { download, isLoading };
}

页面组件中使用

import React from 'react';
import { useDownloadWorkInstructions } from '../hooks/workInstructions/useDownloadWorkInstructions';

export function WorkInstructions() {
    // ...其他业务代码
    const { download: downloadFunc, isLoading } = useDownloadWorkInstructions();

    const handleDownloadFiles = async (workInstructionsId) => {
        try {
            const response = await downloadFunc(workInstructionsId);
            if (response.status === 200) {
                // 补充实际下载逻辑:将响应转为blob并触发浏览器下载
                const blob = await response.blob();
                const url = window.URL.createObjectURL(blob);
                const a = document.createElement('a');
                a.href = url;
                a.download = `work-instructions-${workInstructionsId}.zip`;
                document.body.appendChild(a);
                a.click();
                // 清理临时资源
                window.URL.revokeObjectURL(url);
                document.body.removeChild(a);
            } else {
                console.log('Download error:', response.status);
            }
        } catch (error) {
            console.log('Download error:', error);
        }
    }

    // ...原有渲染代码保持不变
}

备选方案:若坚持使用React Query(不推荐)

需改为手动触发模式,禁用自动执行,并修正参数传递:

// 自定义钩子修改
import { useQuery } from 'react-query';
import { basePath } from '../../config/basePath';

async function downloadWorkInstructions(fileId) {
  return fetch(`${basePath}/files/download-all/${fileId}`, { method: 'GET' });
}

export function useDownloadWorkInstructions() {
  const { isLoading, refetch } = useQuery(
    ['download'],
    () => {}, // 空函数占位,因为手动触发时传参
    { enabled: false } // 禁用自动执行
  );

  const download = async (fileId) => {
    const { data } = await refetch({ queryKey: ['download', fileId], queryFn: () => downloadWorkInstructions(fileId) });
    return data;
  };

  return { download, isLoading };
}
关键修复点总结
  1. 去掉不符合场景的useQuery,改用手动管理加载状态的钩子,贴合点击触发的业务逻辑。
  2. 修正参数传递链,确保fileId能正确传递到fetch请求的URL中。
  3. 补充浏览器端的文件下载逻辑,让接口返回的压缩包能实际保存到本地(原代码仅判断状态,无实际下载动作)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 22:14:52