React下拉框接口请求hook封装

前言

记录一下公司 下拉框封装的 接口hook。主要是支持

  • 初始化或者指定状态下请求接口
  • 防抖搜索
  • 支持不同类型的接口(get、post)

代码

主体 hook

tsx 复制代码
import { IObj, IOption, TRecord } from "@/utils/interface";
import { to } from "@/utils/tools";
import { useMount } from "@quarkunlimit/react-hooks";
import { debounce } from "lodash";
import { useEffect, useRef, useState } from "react";
import {
  IUseMountFetchDataNewProps,
  IUseMountFetchDataResult,
} from "./interface";

/**
 * 初始化请求下拉框接口
 * @param props
 * @returns
 */
export const useSearchSelectFetchNew = (
  props: IUseMountFetchDataNewProps
): IUseMountFetchDataResult => {
  const {
    fetchDataApi,
    request,
    searchParamKey,
    transformOptions,
    refreshFetch,
    initFetch = true,
    needSetExtarData = false,
  } = props;

  const [data, setData] = useState<IOption[]>([]);
  const isMount = useRef<boolean>(false);
  const originData = useRef<IOption[]>([]);

  const fetchData = async (otherRequest?: TRecord) => {
    let newRequst: IObj = {}
    newRequst = {
        page: 1,
        size: 100,
        ...request,
        ...otherRequest
    }
    const [err, res] = await to(
      (() => {
        return fetchDataApi(newRequst);
      })()
    );
    if (!(err || !res)) {
      const data = transformOptions(res);
      setData(data);
      if (!isMount.current) {
        originData.current = data;
        isMount.current = true;
      }
    }
  };

  const onSearch = debounce((value: string) => {
    if (value.trim()) {
      fetchData({
        [searchParamKey]: value,
      });
    } else {
      setData(originData.current);
    }
  }, 500);

  useEffect(() => {
    if (refreshFetch) {
      fetchData();
    }
  }, [refreshFetch]);

  useMount(() => {
    if (initFetch) {
      fetchData();
    }
  });

  const setExtarData = (list: IOption[]) => {
    const newData: IOption[] = [];
    const idSet = new Set<string>();
    for (let item of data) {
      newData.push(item);
      idSet.add(item.value);
    }

    for (let item of list) {
      if (typeof item !== "object") {
        continue;
      }
      if (idSet.has(item?.value)) {
        continue;
      }
      idSet.add(item.value);
      newData.push(item);
    }
    setData(newData);
  };

  return {
    options: data,
    onSearch,
    onFocus: () => fetchData(),
    ...(needSetExtarData ? { setExtarData } : {}),
  };
};

类型定义

ts 复制代码
import { SelectProps } from "antd";

export interface IOption {
  label: string;
  value: string;
  [key: string]: any;
}

export interface IUseMountFetchDataNewProps {
  /**@param 接口Api */
  fetchDataApi: (...arg: any) => Promise<IApiData>;
  /**@param 初始化时接口额外参数 */
  request?: TRecord & { enableFlag?: boolean; dataScopeEnableFlag?: boolean };
  /**@param 搜索时的key */
  searchParamKey: string;
  /**@function 转换数据源为options */
  transformOptions: (res: IApiData) => IOption[];
  /**@param 满足某种条件时加载数据,使用时请将 initFetch 设置为 false  */
  refreshFetch?: boolean;
  /**@param 是否挂载时默认加载数据 */
  initFetch?: boolean;
  /** @param 是否需要setExtarData */
  needSetExtarData?: boolean;
}

export interface IUseMountFetchDataResult extends SelectProps {
  /** @param 下拉框选项 */
  options: IOption[];
  /** @function 下拉框搜索 */
  onSearch: (value: string) => void;
  /** @function 手动添加额外的数据源 */
  setExtarData?: (list: IOption[]) => void;
}

使用

tsx 复制代码
/** @function 获取字典值集合 */
export const sys_dict_value = (params?: IReqSysDictValueDictValue) => {
  return Service.get("/api/business/v1/sys-dict-value/dict-value", {
    params,
  }) as Promise<IResDetail<IResSysDictValueDictValue[]>>;
};

export interface IReqSysDictValueDictValue {
  /** @param 字典编码 */
  dictCode?: string;
  /** @param 字典编码集合 */
  dictCodeList?: string;
  /** @param 字典名称 */
  dictName?: string;
  /** @param 字典值 */
  dictValue?: string;
  /** @param 备注 */
  memo?: string;
}

export interface IResSysDictValueDictValue {
  dictCode: string;
  dictName: string;
  dictValue: string;
  enableFlag: boolean;
  id: string;
  memo: string;
  color: string;
  sortNum: number;
}

const TXDiplomaRadio = function TXDiplomaRadio_({
  initFetch = true,
  refreshFetch = false,
  extraReq = {},
  ...rest
}: ITXDiplomaRadioProps) {
  const { options } = useSearchSelectFetchNew({
    fetchDataApi: sys_dict_value(你的接口,返回promise),
    request: {
      dictCode: "diploma",
      ...extraReq,
    },
    initFetch,
    refreshFetch,
    searchParamKey: "dictName",
    transformOptions: (res) => {
      return res?.data?.map((x: IResSysDictValueDictValue) => ({
        label: x.dictName,
        value: x.dictValue,
      }));
    },
  });

  return <Radio.Group options={options} {...rest} />;
};

export default TXDiplomaRadio;
相关推荐
狗头大军之江苏分军2 小时前
2025,我的"Vibe Coding"时刻:当 AI 成为我的编程搭档
前端
同学807962 小时前
🔥🔥Vue数字翻滚动画组件:让数据展示更具视觉冲击力
前端·vue.js
HashTang2 小时前
【AI 编程实战】第 5 篇:Pinia 状态管理 - 从混乱代码到优雅架构
前端·vue.js·ai编程
青莲8432 小时前
Kotlin Flow 深度探索与实践指南——上部:基础与核心篇
android·前端
Bug生活20482 小时前
五年断更,AI助我半天复活小程序
前端·微信小程序·ai编程
狗头大军之江苏分军2 小时前
Node.js 性能优化实践,但老板只关心是否能跑
前端·后端
恋猫de小郭2 小时前
2025 年终醒悟,AI 让我误以为自己很强,未来程序员的转型之路
android·前端·flutter
用泥种荷花2 小时前
【前端学习AI】PromptTemplate的使用
前端
狗头大军之江苏分军2 小时前
Node.js 真香,但每次部署都想砸电脑
前端·javascript·后端