如何从React自定义Hook返回数据?调用报Invalid hook call错误如何解决
问题原因
- 违反React Hook官方调用规则:Hook仅可在函数组件的顶层作用域调用,禁止在事件回调、循环、条件分支、普通JavaScript函数内部调用。你将自定义Hook
useFetchNGSData放在了事件处理函数handleGetPlayers内部,还嵌套在forEach循环中,因此触发非法Hook调用报错。 - 你的自定义Hook本身结构不存在问题,错误完全由不合规的调用方式导致。
修复方案
如果你需要手动触发请求(比如点击按钮后拉取数据,同时遍历多个teamId批量请求),需要先调整自定义Hook的逻辑,将请求方法暴露出来供外部手动调用,而非默认挂载时自动执行请求:
修改后的自定义Hook代码
import { useState, useCallback } from "react"; const axios = require('axios'); const crypto = require('crypto'); function useFetchNGSData() { const [data, setData] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const dateString = () => { // 原有逻辑保持不变 return date.toString(); } // 把请求逻辑封装为可手动调用的方法 const fetchData = useCallback(async (endpoint) => { setLoading(true); setError(null); try { const ngs_username = process.env.REACT_APP_NGS_USERNAME; const ngs_password = process.env.REACT_APP_NGS_PASSWORD; const ngs_access_token = process.env.REACT_APP_NGS_ACCESS_TOKEN; const ngs_secret_key = process.env.REACT_APP_NGS_SECRET_KEY; const string_to_sign = ngs_username + ngs_password + ngs_access_token + dateString(); const digest = crypto.createHmac('sha1', ngs_secret_key).update(string_to_sign).digest('base64'); const authKey = 'NGS ' + ngs_access_token + ':' + digest; const baseUrl = 'https://api.ngs.com'; axios.defaults.headers.common['Authorization'] = authKey; const response = await axios.get(baseUrl + endpoint); setData(response.data); return response.data; } catch (err) { setError(err); console.error(err); } finally { setLoading(false); } }, []) return { data, loading, error, fetchData }; } export default useFetchNGSData;
组件调用代码修改
将自定义Hook放在组件顶层调用,事件回调中仅调用Hook返回的fetchData方法执行请求:
import React, { useState } from 'react'; import useFetchNGSData from '../useFetchNGSData'; const Data = (props) => { const [players, setPlayers] = useState([]); // Hook必须放在组件顶层作用域调用 const { fetchData } = useFetchNGSData(); const handleGetPlayers = async () => { const allPlayers = []; // 遍历所有队伍ID批量请求数据 for (const teamId of teamCollection) { const teamRoster = await fetchData(`/league/roster/current?teamId=${teamId}`); teamRoster && allPlayers.push(...teamRoster); } setPlayers(allPlayers); } return ( <div> <button onClick={handleGetPlayers}>获取玩家列表</button> {/* 玩家列表渲染逻辑 */} </div> ); } export default Data;
如果你不需要手动触发,只需要在参数变化时自动拉取数据,直接将动态endpoint作为参数传入顶层调用的Hook即可,无需调整Hook原有结构。
内容的提问来源于stack exchange,提问作者sbaden
相关产品推荐
相关产品推荐

