基于Antd <Select> 实现下拉分页,异步加载数据

485 阅读1分钟
/**
 * @author Jack
 * @description
 * @creationDate 2024-09-26
 */
import React, { useState, useEffect } from 'react';
import { Select, Spin, message } from 'antd';
import debounce from 'lodash/debounce';
import { getCustomerListByPage } from 'service';

const { Option } = Select;

// 定义接口类型
interface OptionType {
  value: string;
  customerName: string;
  id: string;
}

interface ApiResponse {
  items: OptionType[];
  hasMore: boolean;
}

const SelectByPage: React.FC = () => {
  const [data, setData] = useState<OptionType[]>([]);
  const [value, setValue] = useState<string | undefined>(undefined);
  const [loading, setLoading] = useState<boolean>(false);
  const [page, setPage] = useState<number>(1);
  const [hasMore, setHasMore] = useState<boolean>(true);

  // 异步获取选项
  const fetchOptions = async (
    searchValue: string,
    pageNum: number
  ): Promise<void> => {
    console.log(556546, pageNum);

    setLoading(true);
    try {
      const {
        data: { records },
      } = await getCustomerListByPage({
        size: 10,
        current: pageNum,
        businessRelations: '02',
        warehouseIds: '',
        customerId: '',
      });

      if (pageNum === 1) {
        setData(records); // 如果是第一页,替换数据
      } else {
        setData((prev) => [...prev, ...records]); // 否则追加数据
      }

      setHasMore(records.length === 10); // 更新是否还有更多数据
    } catch (error) {
      message.error(`Failed to fetch options: ${(error as Error).message}`);
    } finally {
      setLoading(false);
    }
  };

  // 防抖搜索处理
  const handleSearch = debounce((value: string) => {
    setPage(1); // 重置为第一页
    fetchOptions(value, 1); // 请求第一页数据
  }, 300); // 300ms 防抖

  // 下拉框滚动分页加载
  const handleScroll = (e: React.UIEvent<HTMLDivElement, UIEvent>): void => {
    const { target } = e;
    if (
      (target as HTMLDivElement).scrollTop +
        (target as HTMLDivElement).clientHeight >=
      (target as HTMLDivElement).scrollHeight - 8
    ) {
      if (hasMore && !loading) {
        setPage((prev) => {
          const nextPage = prev + 1;
          fetchOptions(value || '', nextPage); // 请求下一页
          return nextPage;
        });
      }
    }
  };

  useEffect(() => {
    fetchOptions('', 1); // 初始化时加载第一页数据
  }, []);

  return (
    <Select
      showSearch
      value={value}
      placeholder="选择"
      onSearch={handleSearch}
      onChange={setValue}
      onPopupScroll={handleScroll}
      notFoundContent={loading ? <Spin size="small" /> : null}
      loading={loading}
      filterOption={false} // 关闭内置的过滤功能
    >
      {data.map((item: OptionType) => (
        <Option key={item.id} value={item.id}>
          {item.customerName}
        </Option>
      ))}
    </Select>
  );
};

export default SelectByPage;