daniel8x
daniel8x

Reputation: 1080

Ant Design Select fetch data at the begining without typing

I am using Ant Design Select.

CodeSandBox URL

According to the example above, when i started typing, then Select will started to load the data using fetchUserList. Is there any way i can make it load all the data at first ? so that when i typing its only search in the loaded list ? I have been thinking about that but no solution. Thank you.

Upvotes: 1

Views: 5727

Answers (2)

Muhammad Junaid
Muhammad Junaid

Reputation: 99

i made a custom hook to fetch data based on scroll Antd gives onPupScroll we can fetch data using this

import { useEffect, useState } from 'react';

import { getAllUsers } from '@api/users';

export default function usefetchUsers(pageNumber: number, limit: number) {
  const [userLoading, setLoading] = useState(true);
  const [usersError, setUserError] = useState(false);
  const [users, setUsers] = useState([]);
  const [hasMore, setHasMore] = useState(false);

  useEffect(() => {
    setLoading(true);
    setUserError(false);
    fetchAllUsers();
  }, [pageNumber]);

  const fetchAllUsers = async () => {
    setLoading(true);
    const allUsers = await getAllUsers(pageNumber, limit);
    if (allUsers?.hasError) {
      setUsers([...users]);
      setUserError(allUsers?.errorMessage);
      setLoading(false);
      setHasMore(false);
    } else {
      if (allUsers && allUsers?.data?.length > 0) {
        const usersData = allUsers?.data?.map((item) => {
          return {
            value: item.id,
            label: item.name,
          };
        });
        const checkMore = Boolean(parseInt(allUsers?.data?.page?.totalPages) > 1);
        setHasMore(checkMore);
        setLoading(false);
        setUsers([...users, ...usersData]);
      }
    }
  };

  return { userLoading, usersError, users, hasMore };
}



Usage


const { users: newUsers, hasMore } = usefetchUsers(pageNumber, limit, false);

OnScroll triggers this function

const onScroll = async (event, type) => {
    const target = event.target;
    const V = Math.ceil(target.scrollTop) + target.offsetHeight;
    console.log(V);

    if (V === target.scrollHeight) {
      target.scrollTo(0, target.scrollHeight);
      const page = pageNumber + 1;
      if (isMore) {
        setUsersLoading(true);
        const allUsers = await getAllUsers(page, limit);
        if (allUsers?.hasError) {
          setUsers([...users]);
          setUserError(allUsers?.errorMessage);
          setUsersLoading(false);
          setIsMore(false);
        } else {
          if (allUsers && allUsers?.data?.data.length > 0) {
            const usersData = allUsers?.data?.data.map((item) => {
              return {
                value: item.id,
                label: item.name,
              };
            });
            const checkMore = Boolean(parseInt(allUsers?.data?.page?.totalPages) > 1);
            setIsMore(checkMore);
            setIsMore(false);
            setUsers([...users, ...usersData]);
            setUsersLoading(false);
          }
        }
      }

}

Select Component usgae

 <FieldWrapper
            label={'Select Supplier'}
            error={supplierError && 'Please select a Supplier.'}
          >
            <Select
              style={{
                width: '100%',
              }}
              loading={supplierLoading}
              mode="multiple"
              options={Suppliers}
              onChange={handleSupplierSelect}
              value={selectedSuppliers}
              onPopupScroll={(e) => onScroll(e, 'supplier')}
              maxTagCount={'responsive'}
              placeholder="Select a Supplier"
            />
          </FieldWrapper>

Upvotes: 0

Anees Hikmat Abu Hmiad
Anees Hikmat Abu Hmiad

Reputation: 3550

Base on Your code, you are using denounce select, and the idea of this type of select its search on list and fetch via API and used callback to print the search result, and its handling via denounce to reduce number of request...

So that, I see its not correct to use it to fetch all data then trigger search again, if you need that, just you need to use normal antd select, so you can search and list will be prediene or fetch or pass by state...

Example one for normal select:

import { Select, Spin } from "antd";

const { Option } = Select;

const children = [];
for (let i = 10; i < 36; i++) {
  children.push(<Option key={i.toString(36) + i}>{i.toString(36) + i}</Option>);
}

function handleChange(value) {
  console.log(`selected ${value}`);
}

ReactDOM.render(
  <Select mode="tags" style={{ width: "100%" }} onChange={handleChange}>
    {children}
  </Select>,
  document.getElementById("container")
);

Now, back to your example, if you still need it, you can build function to fetch needed data and put it on options like this:

options.push({ label: "Anees 1", value: "Anees", })

for example if options is empty, and still search is not start push these data...just for an example I added it in simple way to explan how you can invoke it

import React from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import './index.css';
import { Select, Spin } from 'antd';
import debounce from 'lodash/debounce';

function DebounceSelect({ fetchOptions, debounceTimeout = 800, ...props }) {
  const [fetching, setFetching] = React.useState(false);
  const [options, setOptions] = React.useState([]);
  const fetchRef = React.useRef(0);
  const debounceFetcher = React.useMemo(() => {
    const loadOptions = (value) => {
      fetchRef.current += 1;
      const fetchId = fetchRef.current;
      setOptions([]);
      setFetching(true);
      fetchOptions(value).then((newOptions) => {
        if (fetchId !== fetchRef.current) {
          // for fetch callback order
          return;
        }

        setOptions(newOptions);
        setFetching(false);
      });
    };

    return debounce(loadOptions, debounceTimeout);
  }, [fetchOptions, debounceTimeout]);

  console.log(options);
  if(options.length === 0){// Example condition, its not fully handling
    options.push({
      label: "Anees 1",
      value: "Anees",
    })
  }

  console.log(options);

  return (
    <Select
      labelInValue
      filterOption={false}
      onSearch={debounceFetcher}
      notFoundContent={fetching ? <Spin size="small" /> : null}
      {...props}
      options={options}
    />
  );
} // Usage of DebounceSelect

async function fetchUserList(username) {
  console.log('fetching user', username);
  return fetch('https://randomuser.me/api/?results=5')
    .then((response) => response.json())
    .then((body) =>
      body.results.map((user) => ({
        label: `${user.name.first} ${user.name.last}`,
        value: user.login.username,
      })),
    );
}

const Demo = () => {
  const [value, setValue] = React.useState([]);
  return (
    <DebounceSelect
      mode="multiple"
      value={value}
      placeholder="Select users"
      fetchOptions={fetchUserList}
      onChange={(newValue) => {
        setValue(newValue);
      }}
      style={{
        width: '100%',
      }}
    />
  );
};

ReactDOM.render(<Demo />, document.getElementById('container')); 

Upvotes: 2

Related Questions