react+ts+vite+axios简单搭建

96 阅读2分钟

配置: .env

VITE_API_URL=https://ku.qingnian8.com

App.tsx

import "./App.css";
import WzList from "./pages/channl";

function App() {
  return (
    <>
      <WzList></WzList>
    </>
  );
}

export default App;

src/pages/channl/index.tsx

import React from "react";
import { useEffect } from "react";
import { useWzList } from "../../hooks/useChannl";

// const WzList: React.FC = () => {
//   const { wzList, loading } = useWzList(10, "16");
//   console.log("获取的数据", wzList);

//   if (loading) return <div>Loading...</div>;

//   return (
//     <div>{JSON.stringify(wzList)}</div>
//     // <ul>
//     //   {wzList.map((item) => (
//     //     <li key={item?.id}>{item?.name}</li>
//     //   ))}
//     // </ul>
//   );
// };

// export default WzList;

const WzList: React.FC = () => {
  const { wzList, loading, error, refetch } = useWzList(10, "16");

  // 只在数据变化时记录
  useEffect(() => {
    // if (wzList.length > 0) {
    console.log("获取的数据", wzList);
    // }
  }, [wzList]);

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

  return (
    <div>
      <button onClick={refetch}>刷新数据</button>
      <div>{JSON.stringify(wzList)}</div>
    </div>
  );
};

export default WzList;

src/hooks/useChannl.tsx

import { useState, useEffect, useCallback } from "react";
import { getWzList } from "../api/channl";
import type { WzList } from "../types/channl";

// export const useWzList = (num: number, cid: string) => {
//   const [wzList, setWzList] = useState<WzList[]>([]);
//   const [loading, setLoading] = useState(false);

//   // 使用 useCallback 避免重复创建函数
//   const fetchWzList = useCallback(async () => {
//     setLoading(true);
//     try {
//       const data = await getWzList(num, cid);
//       setWzList(data);
//     } catch (err) {
//       console.error("获取文章列表错误:", err);
//     } finally {
//       setLoading(false);
//     }
//   }, [num, cid]); // 添加依赖项

//   useEffect(() => {
//     fetchWzList();
//   }, []);

//   return {
//     wzList,
//     loading,
//     // refetch: fetchWzList,
//   };
// };

// 修改后的 useWzList
export const useWzList = (num: number, cid: string) => {
  const [wzList, setWzList] = useState<WzList[]>([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null); // 添加错误状态

  useEffect(() => {
    const fetchData = async () => {
      setLoading(true);
      setError(null);
      try {
        const data = await getWzList(num, cid);
        setWzList(data);
      } catch (err: any) {
        setError("加载文章列表失败");
        console.error("获取文章列表错误:", err);
      } finally {
        setLoading(false);
      }
    };

    fetchData();

    // 清理函数:组件卸载时取消请求
    return () => {};
  }, [num, cid]); // 依赖 num 和 cid

  // 独立的重取函数(不包含在 useEffect 中)
  const refetch = useCallback(async () => {
    setLoading(true);
    setError(null);
    try {
      const data = await getWzList(num, cid);
      setWzList(data);
    } catch (err) {
      setError("重新加载失败");
      console.error("重新加载错误:", err);
    } finally {
      setLoading(false);
    }
  }, [num, cid]);

  return {
    wzList,
    loading,
    error,
    refetch,
  };
};

src/api/channl.ts

import { get, post } from "../utils/request";
import type { WzList } from "../types/channl";

// 获取用户
const getWzList = async (num: number, cid: string): Promise<WzList[]> => {
  return get<WzList[]>("/wxList.php", {
    params: { num, cid },
  });
};

export { getWzList };

src/utils/request.ts

import axios, {
  type InternalAxiosRequestConfig,
  type AxiosResponse,
  AxiosHeaders,
  AxiosError,
} from "axios";

// 1. 创建 Axios 实例
const request = axios.create({
  baseURL: import.meta.env.VITE_API_URL,
  timeout: 10000,
  headers: { "Content-Type": "application/json" },
});

// 2. 请求拦截器 (统一添加Token)[1](@ref)
request.interceptors.request.use((config: InternalAxiosRequestConfig) => {
  //   const token = localStorage.getItem("token");
  //   if (token) {
  config.data = JSON.stringify(config.data);

  config.headers = AxiosHeaders.from({
    "Content-Type": "application/json",
    ...config.headers,
  });
  //   }
  return config;
});

// 3. 错误统一处理器 (核心分离点)
const errorHandler = (error: AxiosError) => {
  if (error.response) {
    // 服务器响应错误[6,7](@ref)
    const status = error.response.status;
    const errorMap: Record<number, string> = {
      400: "请求参数错误",
      401: "未授权,请重新登录",
      403: "禁止访问",
      404: "资源不存在",
      500: "服务器错误",
    };
    return Promise.reject(errorMap[status] || `服务器错误(${status})`);
  } else if (error.request) {
    // 请求已发送但无响应[6](@ref)
    return Promise.reject("网络连接超时,请检查网络");
  }
  // 其他错误[6](@ref)
  return Promise.reject("请求配置错误");
};

// 4. 封装GET方法
export const get = async <T>(url: string, params?: object): Promise<T> => {
  try {
    const response: AxiosResponse<T> = await request.get(url, { params });
    return response.data;
  } catch (error) {
    return Promise.reject(errorHandler(error as AxiosError));
  }
};

// 5. 封装POST方法
export const post = async <T>(url: string, data?: object): Promise<T> => {
  try {
    const response: AxiosResponse<T> = await request.post(url, data);
    return response.data;
  } catch (error) {
    return Promise.reject(errorHandler(error as AxiosError));
  }
};

src/types.ts

export interface WzList {
  code: number;
  data: string;
}