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 }; }
关键修复点总结
- 去掉不符合场景的useQuery,改用手动管理加载状态的钩子,贴合点击触发的业务逻辑。
- 修正参数传递链,确保
fileId能正确传递到fetch请求的URL中。 - 补充浏览器端的文件下载逻辑,让接口返回的压缩包能实际保存到本地(原代码仅判断状态,无实际下载动作)。
内容的提问来源于stack exchange,提问作者Daniel Corona
相关产品推荐
相关产品推荐

