【无标题】

第一步:封装企业级 Axios (request.js)

企业级 Axios 通常需要处理:环境变量、请求拦截(加 Token)、响应拦截(统一错误处理)、超时设置以及取消请求的能力。

javascript 复制代码
// src/utils/request.js
import axios from 'axios';
import { message } from 'antd'; // 假设你用了 antd,或者用 alert

// 1. 创建实例
const service = axios.create({
  baseURL: process.env.REACT_APP_API_BASE_URL || '/api', // 环境变量
  timeout: 10000, // 请求超时时间
});

// 2. 请求拦截器 (Request Interceptor)
service.interceptors.request.use(
  (config) => {
    // 在这里添加 Token 或其他公共参数
    const token = localStorage.getItem('token');
    if (token) {
      config.headers['Authorization'] = `Bearer ${token}`;
    }
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

// 3. 响应拦截器 (Response Interceptor)
service.interceptors.response.use(
  (response) => {
    // 假设后端返回格式统一为 { code: 200, data: [], msg: '' }
    const res = response.data;
    
    if (res.code !== 200) {
      message.error(res.msg || '请求失败');
      
      // 特定错误处理,例如 401 跳转登录
      if (res.code === 401) {
        window.location.href = '/login';
      }
      return Promise.reject(new Error(res.msg));
    }
    return res; // 直接返回 data 部分,业务层拿到的就是纯净数据
  },
  (error) => {
    message.error(error.message || '网络异常');
    return Promise.reject(error);
  }
);

export default service;

第二步:封装通用的请求 Hook (useRequest.js)

这个 Hook 参考了你图片中的写法,但它做了增强:支持手动触发 (manual)、依赖刷新 (refreshDeps) 和参数透传。

javascript 复制代码
// src/hooks/useRequest.js
import { useState, useEffect, useCallback } from 'react';

/**
 * 通用的请求 Hook
 * @param {Function} api - 请求函数,必须返回一个 Promise
 * @param {Array} defaultParams - 默认参数
 * @param {Object} options - 配置项
 */
function useRequest(api, defaultParams = [], options = {}) {
  const { manual = false, refreshDeps = [] } = options;

  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(!manual);
  const [error, setError] = useState(null);

  // 请求函数
  const run = useCallback(
    async (...args) => {
      setLoading(true);
      setError(null);
      try {
        // 合并参数:默认参数 + 手动传入参数
        const finalParams = [...defaultParams, ...args];
        const res = await api(...finalParams);
        setData(res); // 注意:这里依赖 axios 拦截器返回了 res.data
        return res;
      } catch (err) {
        setError(err);
        throw err; // 抛出错误,让组件能捕获
      } finally {
        setLoading(false);
      }
    },
    [api, ...defaultParams] // 依赖项
  );

  // 自动执行逻辑
  useEffect(() => {
    if (!manual) {
      run();
    }
  }, [run, manual, ...refreshDeps]);

  return {
    data,
    loading,
    error,
    run,
  };
}

export default useRequest;

第三步:封装具体的业务 API 函数

这一步是为了把 axios 和具体的 URL 隔离,方便后期维护(比如后端改接口名,只改这里)。

javascript 复制代码
// src/api/user.js
import request from '@/utils/request';

// 导出请求函数,而不是直接调用
export const getUserList = (params) => {
  return request.get('/users', { params });
};

export const deleteUser = (id) => {
  return request.delete(`/users/${id}`);
};

第四步:组件调用模板(完全复刻你图片的风格)

现在,在组件中使用它就会非常清爽,完美匹配你的需求。

jsx 复制代码
// UserPage.jsx
import React from 'react';
import { getUserList } from '@/api/user';
import useRequest from '@/hooks/useRequest';

function UserPage() {
  // 🎯 重点:完全类似你图片中的调用方式
  // 第一个参数传 api 函数,第二个参数传默认参数,第三个参数传 options
  const { data, loading, error, run } = useRequest(getUserList, [], {
    // manual: true, // 如果需要手动触发,设为 true
  });

  // 手动触发示例(如果设置了 manual: true)
  const handleRefresh = () => {
    run(); 
  };

  if (loading) return <div>加载中...</div>;
  if (error) return <div>出错了: {error.message}</div>;

  return (
    <div>
      <h1>用户列表</h1>
      <button onClick={handleRefresh}>刷新数据</button>
      <ul>
        {data?.list?.map((user) => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
}

export default UserPage;

总结:架构分层

  1. request.js:纯粹的 HTTP 工具,不管业务逻辑,只负责底层通信。
  2. api/*.js:接口契约,定义 URL 和参数结构。
  3. hooks/useRequest.js:逻辑复用层,处理 Loading、Error、重试等通用逻辑。
  4. Component :纯粹的 UI 层,只需要关心 dataloading,调用 run 即可。

这种结构非常稳定,在大型 React 项目中是标准的工程化实践。

相关推荐
周末也要写八哥2 分钟前
线程的生命周期之“守护“线程
java·开发语言·jvm
该用户已成仙8 分钟前
vue3 使用 vuedraggable 报错 TypeError: isFunction2 is not a function
前端·javascript·vue.js
aidou13148 分钟前
Kotlin中实现星级评价选择功能(仅支持整数)
前端·kotlin·自定义view·imageview·ontouchevent·customratingbar
良逍Ai出海12 分钟前
我用 Codex 搭了一个 WordPress 独立站
前端
.千余13 分钟前
【C++】C++继承入门(上):继承语法与基本特性详解
开发语言·c++·笔记·学习·其他
TPBoreas13 分钟前
前端面试问题打把-场景题
开发语言·前端·javascript
问心无愧051313 分钟前
ctf show web入门159
前端·笔记
恋猫de小郭18 分钟前
Flutter 又为 AI 时代添砖加瓦:全新 ComponentLibrary 提议
android·前端·flutter
skywalk816320 分钟前
段言的设计文档:中文编程赛道的竞争格局,谁在牌桌上?
开发语言·学习·编程
就叫_这个吧20 分钟前
HTML或JSP页面链接CSS,link标签没问题,但不显示样式问题解决
java·前端·css·html·intellij-idea·jsp