antd select组件支持数据懒加载(分页)和远程搜索

1,681 阅读1分钟
import React, { useState, useMemo, useRef, useEffect } from 'react';
import { Select, Spin } from 'antd';
import debounce from 'lodash/debounce';
import request from '@/utils/request';

//分页计算 如果 总条数/每页数量的商 > 取整后的商,那么商加1,否则就是现在的商
// 总条数,每页条数
function calcPageNum(count, pageSize) {
  const num = count / pageSize;
  return num > parseInt(num) ? parseInt(num) + 1 : parseInt(num);
}

function DebounceSelect({ fetchOptions, debounceTimeout = 800, ...props }) {
  const [fetching, setFetching] = useState(false);
  const [options, setOptions] = useState();
  const [totalPage, setTotalPage] = useState(1);
  const fetchRef = useRef(0);
  const [params, setParams] = useState({
    page: 1,
    per_page: 20,
    q: '',
  });
  const debounceFetcher = useMemo(() => {
    const loadOptions = (value) => {
      fetchRef.current += 1;
      const fetchId = fetchRef.current;
      setOptions([]);
      setFetching(true);
      fetchOptions({
        ...params,
        q: value,
      }).then(({ count, opts }) => {
        if (fetchId !== fetchRef.current) {
          // for fetch callback order
          console.log('哈哈哈搜索');
          return;
        }
        if (count === 0) {
          setFetching(true);
          setTotalPage(1);
          setOptions(opts);
          return;
        }
        setTotalPage(calcPageNum(count, params.per_page));
        setOptions(opts);
        setFetching(false);
      });
    };

    return debounce(loadOptions, debounceTimeout);
  }, [fetchOptions, debounceTimeout]);
  useEffect(() => {
    fetchOptions(params).then(({ count, opts }) => {
      setTotalPage(calcPageNum(count, params.per_page));
      setOptions(opts);
      setFetching(false);
      props.onChange(opts[0]);
    });
  }, []);
  const onScroll = (e) => {
    const { scrollHeight, clientHeight, scrollTop } = e.target;
    if (totalPage > params.page) {
      if (clientHeight + scrollTop === scrollHeight) {
        setParams({
          ...params,
          page: params.page + 1,
        });
        fetchOptions({ ...params, page: params.page + 1 }).then(({ count, opts }) => {
          setTotalPage(calcPageNum(count, params.per_page));
          setOptions([...options, ...opts]);
          setFetching(false);
        });
      }
    }
  };
  return (
    <Select
      labelInValue
      allowClear
      filterOption={false}
      onSearch={debounceFetcher}
      notFoundContent={
        fetching ? (
          <div>
            <Spin size="small" />
            当前搜索无结果,请输入有效关键词
          </div>
        ) : null
      }
      onPopupScroll={onScroll}
      {...props}
      options={options}
    />
  );
} // Usage of DebounceSelect

const getNovelsApi = '/novel/novels/searchForPromLink';

const SelectLay = (props) => {
  const [value, setValue] = useState({
    label: '',
    value: '',
  });
  const getNovels = async (params) => {
    return request(`${getNovelsApi}?display=json`, {
      method: 'GET',
      params: { ...params },
    }).then((res) => {
      if (res.data.count === 0) {
        return {
          count: res.data.count,
          opts: [],
        };
      } else {
        return {
          count: res.data.count,
          opts: res.data.novel_list.map((item) => ({
            value: item.novel_id,
            label: item.title,
          })),
        };
      }
    });
  };

  const changeVal = (val) => {
    setValue(val);
    props.onChange('int', '=', val);
  };
  return (
    <>
      <label style={{ marginRight: '10px' }}>{props.label}</label>
      <DebounceSelect
        mode="multiple"
        showSearch={true}
        value={value}
        placeholder="请选择"
        fetchOptions={getNovels}
        onChange={changeVal}
        style={{
          width: '360px',
        }}
      />
    </>
  );
};
export default SelectLay;