React中API请求文件分离:解决Invalid hook call错误
问题原因
你遇到的「Invalid hook call」错误,核心原因是React Hook(比如useState)只能在React组件的顶层,或者命名以use开头的自定义Hook中调用,而你写的GetUsers是普通函数,完全不符合Hook的使用规则。
正确设计方案
下面提供两种最常用的合理实现方式,你可以根据业务场景选择:
方案1:API层与状态层分离(推荐通用场景)
把Api.js做成纯请求工具,只负责发送请求和处理响应格式,状态管理交给React组件自己完成。
改造Api.js(纯请求函数)
// Api.js import axios from "axios"; // 统一封装API请求,返回Promise供组件处理 export const getUsers = () => { return axios.get("http://localhost:8080/api/Users", { headers: { "x-access-token": localStorage.getItem("token"), "Content-Type": "application/json" } }) .then(res => { if (res.statusText === "OK") { return res.data; } // 请求成功但状态不符合预期,抛出错误 throw new Error(`请求异常: ${res.statusText}`); }) .catch(err => { console.error("请求错误: ", err); // 抛出错误让组件捕获处理 throw err; }); };
改造ShowData.js(组件内管理状态)
// ShowData.js import { useEffect, useState } from "react"; import { getUsers } from "./Api"; const Properties = () => { // 组件内声明状态,管理数据、加载状态、错误信息 const [users, setUsers] = useState([]); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); useEffect(() => { // 用async/await处理异步请求 const fetchUsers = async () => { setIsLoading(true); try { const userData = await getUsers(); setUsers(userData); } catch (err) { setError(err.message); } finally { setIsLoading(false); } }; fetchUsers(); }, []); // 根据状态渲染不同内容 if (isLoading) return <div>加载中...</div>; if (error) return <div>出错了: {error}</div>; return ( <div> <h2>用户列表</h2> <ul> {users.map(user => ( <li key={user.id}>{user.name}</li> ))} </ul> </div> ); }; export default Properties;
方案2:封装成自定义Hook(适合复用场景)
如果多个组件都需要调用该接口,且都要处理加载、错误、数据状态,可以把逻辑封装成自定义Hook,实现状态逻辑复用。
创建自定义Hook(useUsers.js)
// useUsers.js import { useState, useEffect } from "react"; import axios from "axios"; // 自定义Hook必须以use开头 export const useUsers = () => { const [users, setUsers] = useState([]); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); useEffect(() => { const fetchUsers = async () => { setIsLoading(true); try { const res = await axios.get("http://localhost:8080/api/Users", { headers: { "x-access-token": localStorage.getItem("token"), "Content-Type": "application/json" } }); if (res.statusText === "OK") { setUsers(res.data); } else { throw new Error(`请求异常: ${res.statusText}`); } } catch (err) { setError(err.message); console.error("请求错误: ", err); } finally { setIsLoading(false); } }; fetchUsers(); }, []); // 返回状态和数据供组件使用 return { users, isLoading, error }; };
在组件中使用自定义Hook
// ShowData.js import { useUsers } from "./useUsers"; const Properties = () => { // 直接调用自定义Hook获取状态 const { users, isLoading, error } = useUsers(); if (isLoading) return <div>加载中...</div>; if (error) return <div>出错了: {error}</div>; return ( <div> <h2>用户列表</h2> <ul> {users.map(user => ( <li key={user.id}>{user.name}</li> ))} </ul> </div> ); }; export default Properties;
额外注意点
- 原代码中存在命名不一致问题:
GetUsers(大驼峰)和调用时的getUsers(小驼峰),已在修正代码中统一为小驼峰(符合JS函数命名规范)。 - 原Promise的
then块语法有误(else位置错误),已在修正代码中修复。
内容的提问来源于stack exchange,提问作者J.D.
相关产品推荐
相关产品推荐

