【无标题】

第一步:封装企业级 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 项目中是标准的工程化实践。

相关推荐
泯泷32 分钟前
第 2 篇:设计第一套字节码:Opcode、Instruction 与 Constant Pool
前端·javascript·安全
妙码生花32 分钟前
从 PHP 到 AI + Golang,程序员自救转型手记(十五):优化细节、网络请求封装
前端·后端·ai编程
泯泷33 分钟前
第 1 篇:从 1 + 2 开始:亲手写出第一台 JSVM
前端·javascript·安全
团团崽_七分甜34 分钟前
Spring Boot 核心知识点总结
前端
lichenyang4531 小时前
从一个按钮开始,理解 ASCF 框架到底在做什么
前端
古夕1 小时前
第三方 SSO 接入实践:redirect_uri 编码、回调一致性与跨项目联调
前端·vue.js
朦胧之1 小时前
页面白屏卡住排查方法
前端·javascript
用户593608741401 小时前
Playwright 黑魔法:用 ClipboardEvent 绕过 React 富文本编辑器
前端
石山岭2 小时前
自己动手写了一个 Android 虚拟定位 App:GPSSimulate 技术实
android·前端
犇驫聊AI2 小时前
Chrome DevTools MCP + Claude Code 自定义skills生成接口代码生成器
前端·javascript