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

新手开发者:组件专属API请求文件的最佳实践咨询

React API 请求文件/目录结构最佳实践

你的两种实现思路都有各自的适用场景,没有绝对的「最优解」,核心看项目规模和复杂度:

一、你当前的自定义 Hook 聚合方式

这种方式把数据状态和API 操作逻辑封装在一起,优点很明显:

  • 组件使用时成本极低,直接调用 useLeads() 就能拿到数据和增删方法,不用自己写状态管理和请求触发逻辑
  • 逻辑集中,单个模块(比如 Leads)的所有相关代码都在一个文件里,小型项目里维护起来很方便

但缺点也会随着项目变大逐渐显现:

  • API 逻辑和状态耦合在一起,没法在非组件环境(比如工具函数、批量处理脚本)里复用这些请求
  • 如果后续新增更多 API(比如修改 Lead、筛选 Lead),这个 Hook 会越来越臃肿,代码可读性下降

二、拆分独立 API 函数的方式

不管是把多个请求放在单个文件导出,还是每个请求单独建文件,本质都是把 API 逻辑从状态管理中剥离出来,优点是:

  • API 函数是纯逻辑,只负责发请求、处理响应,可在任何地方复用,也更容易单独写单元测试
  • 职责单一,每个函数只做一件事,代码结构更清晰

但缺点是:

  • 组件里使用时需要自己处理状态(比如用 useState 存数据、useEffect 触发请求),会产生不少重复代码
  • 状态分散在各个组件里,跨组件共享数据时会很麻烦

三、更推荐的分层方案(中大型项目首选)

把「API 服务层」和「状态管理 Hook」完全拆分,兼顾复用性和易用性:

1. 单独封装 API 服务层

创建 src/services/leads.js,只处理纯 API 请求逻辑,不涉及任何 React 状态:

import axios from 'axios';

const BASE_URL = 'http://localhost:5000/leads';

export const fetchLeads = async () => {
  const response = await axios.get(BASE_URL);
  return response.data;
};

export const addLead = async (newLead) => {
  const response = await axios.post(`${BASE_URL}/new`, newLead);
  if (response.status !== 200) throw new Error('添加 Lead 失败');
  return response.data;
};

export const deleteLead = async (name, email) => {
  const response = await axios.delete(`${BASE_URL}/delete`, {
    params: { name, email }
  });
  if (response.status !== 200) throw new Error('删除 Lead 失败');
  return response.data;
};

2. 封装状态管理 Hook

创建 src/hooks/useLeads.js,调用上面的 API 函数,专门处理状态和业务逻辑:

import { useState, useEffect } from "react";
import { fetchLeads, addLead, deleteLead } from '../services/leads';

export function useLeads() {
  const [leads, setLeads] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  // 加载数据逻辑
  const loadLeads = async () => {
    setLoading(true);
    try {
      const data = await fetchLeads();
      setLeads(data);
      setError(null);
    } catch (err) {
      setError(err.message);
      console.error(err);
    } finally {
      setLoading(false);
    }
  };

  // 初始化加载
  useEffect(() => {
    loadLeads();
  }, []);

  // 添加 Lead 逻辑
  const handleAddLead = async (newLead) => {
    try {
      const data = await addLead(newLead);
      setLeads(prev => [...prev, data]);
    } catch (err) {
      setError(err.message);
      console.error(err);
    }
  };

  // 删除 Lead 逻辑
  const handleDeleteLead = async (name, email) => {
    try {
      await deleteLead(name, email);
      setLeads(prev => prev.filter(lead => lead.name !== name && lead.email !== email));
    } catch (err) {
      setError(err.message);
      console.error(err);
    }
  };

  return { leads, loading, error, loadLeads, handleAddLead, handleDeleteLead };
}

3. 推荐的目录结构(中大型项目)

src/
  services/       # 按业务模块拆分的纯 API 服务
    leads.js
    users.js
    orders.js
  hooks/          # 封装状态和业务逻辑的自定义 Hook
    useLeads.js
    useUsers.js
  components/     # 纯 UI 组件,只负责渲染,调用 Hook 获取数据和方法
    LeadList.jsx
    LeadForm.jsx

总结

  • 小型项目:直接用你当前的自定义 Hook 方式完全没问题,快速高效
  • 中大型项目:优先选择「API 服务层 + 状态 Hook」的分层方案,职责清晰,复用性强,后期维护成本低

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 13:08:14