Search code examples
reactjsselectantdreact-lifecycle-hooks

Ant Select. the list popup doesn't appear sometime


Using ant Select, I've written a component, which has two modes. first: it takes options which is select data. and display it(this part is working fine). second: it takes a select_url and the component itself sends request to fetch data from server. Every thing works fine except when I search characters, sometimes the list popup doesn't show up!!!! which is un expected. I'm sure data reaches the setOptions and even _options has data, but it doesn't show up. but, when I search numbers, it works fine!

I'm new to react, I used to use Vuejs. is it possible that I'm not using react properly ?

Every thing works fine except when I search characters, sometimes the list popup doesn't show up!!!! which is un expected. I'm sure data reaches the setOptions and even _options has data, but it doesn't show up. but, when I search numbers, it works fine!

here is my hole component:

import React, {useEffect, useState} from "react";
import {Select, Spin} from "antd";
import {SelectOption} from "../../@types/app";

interface CustomSelectProps {
    options?: SelectOption[],
    placeholder?: string,
    select_url?: string
}

const CustomSelect: React.FC<CustomSelectProps> = (
    {
        options,
        placeholder = "انتخاب کنید",
        select_url
    }
) => {
    const [_options, setOptions] = useState<undefined | SelectOption[]>(options);
    const [nextPageUrl, setNextPageUrl] = useState<string | null>(null);
    const [loading, setLoading] = useState<boolean>(false);

    useEffect(() => {
        if (select_url) {
            search()
        } else if (options && !select_url) {
            setOptions(options);
        } else {
            // todo: notify user, that this component needs at least one of options || select_url.
            console.log("specify data. SELECT");
        }
    }, []);

    const search = async (input: string = '') => {
        if (!select_url) return;

        setLoading(true);
        setOptions(() => []);

        const url = `${select_url}?search=${input}&page=1`;

        try {
            const response = await fetch(url);
            if (!response.ok) {
                return new Error(`Network response was not ok (${response.status})`);
            }
            const data = await response.json();
            setNextPageUrl(data.next);
            setOptions(prevOptions => [...prevOptions, ...data.data]);
        } catch (error) {
            console.error('Error fetching data:', error);
        } finally {
            setLoading(false);
        }
    };

    const handlePopupScroll = async (e: React.UIEvent<HTMLDivElement>) => {
        if (!select_url) return;

        const popupContainer = e.currentTarget;
        const isAtEndOfScroll = popupContainer.scrollTop + popupContainer.clientHeight === popupContainer.scrollHeight;

        if (isAtEndOfScroll && nextPageUrl && !loading) {
            try {
                const res = await (await fetch(nextPageUrl)).json();
                setNextPageUrl(res.next);
                const {data} = res;
                setOptions((prevState) => {
                    return [...prevState, ...data];
                });
            } catch (e) {
                console.log(e);
            } finally {
                setLoading(false);
            }
        }
    }

    // filter options based on user input in client mode.
    const handleFilterOption = (input: string, option?: SelectOption) => (option?.label ?? '').toLowerCase().includes(input.toLowerCase());

    return (
        <Select
            options={_options}
            placeholder={placeholder}
            showSearch={true}
            filterOption={select_url ? undefined : handleFilterOption}
            onSearch={search}
            notFoundContent={loading ? <Spin size="small"/> : null}
            virtual={true}
            onPopupScroll={handlePopupScroll}
        >
        </Select>
    );
}

export default CustomSelect;

and here is how data comes:

[
   { label: "test", value: "value", key: "1" },
]  

Solution

  • You should use optionFilterProp to select where select component will search and filter based on what are you typing.

    As said in docs:

    optionFilterProp - Which prop value of option will be used for filter if filterOption is true. If options is set, it should be set to label

    Hope this helps :)