Reputation: 1080
I am using Ant Design Select.
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
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
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