mirror of
https://github.com/ant-design/ant-design.git
synced 2025-06-11 03:22:59 +08:00

* docs: move mock api from randomuser.me to mocky.io * update * update * update * test: update snapshot
104 lines
2.7 KiB
TypeScript
104 lines
2.7 KiB
TypeScript
import React, { useMemo, useRef, useState } from 'react';
|
|
import { Select, Spin, Avatar } from 'antd';
|
|
import type { SelectProps } from 'antd';
|
|
import debounce from 'lodash/debounce';
|
|
|
|
export interface DebounceSelectProps<ValueType = any>
|
|
extends Omit<SelectProps<ValueType | ValueType[]>, 'options' | 'children'> {
|
|
fetchOptions: (search: string) => Promise<ValueType[]>;
|
|
debounceTimeout?: number;
|
|
}
|
|
|
|
function DebounceSelect<
|
|
ValueType extends {
|
|
key?: string;
|
|
label: React.ReactNode;
|
|
value: string | number;
|
|
avatar?: string;
|
|
} = any,
|
|
>({ fetchOptions, debounceTimeout = 300, ...props }: DebounceSelectProps<ValueType>) {
|
|
const [fetching, setFetching] = useState(false);
|
|
const [options, setOptions] = useState<ValueType[]>([]);
|
|
const fetchRef = useRef(0);
|
|
|
|
const debounceFetcher = useMemo(() => {
|
|
const loadOptions = (value: string) => {
|
|
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]);
|
|
|
|
return (
|
|
<Select
|
|
labelInValue
|
|
filterOption={false}
|
|
onSearch={debounceFetcher}
|
|
notFoundContent={fetching ? <Spin size="small" /> : 'No results found'}
|
|
{...props}
|
|
options={options}
|
|
optionRender={(option) => (
|
|
<div style={{ display: 'flex', alignItems: 'center' }}>
|
|
{option.data.avatar && <Avatar src={option.data.avatar} style={{ marginRight: 8 }} />}
|
|
{option.label}
|
|
</div>
|
|
)}
|
|
/>
|
|
);
|
|
}
|
|
|
|
// Usage of DebounceSelect
|
|
interface UserValue {
|
|
label: string;
|
|
value: string;
|
|
avatar?: string;
|
|
}
|
|
|
|
async function fetchUserList(username: string): Promise<UserValue[]> {
|
|
console.log('fetching user', username);
|
|
return fetch(`https://660d2bd96ddfa2943b33731c.mockapi.io/api/users/?search=${username}`)
|
|
.then((res) => res.json())
|
|
.then((res) => {
|
|
const results = Array.isArray(res) ? res : [];
|
|
return results.map((user) => ({
|
|
label: user.name,
|
|
value: user.id,
|
|
avatar: user.avatar,
|
|
}));
|
|
});
|
|
}
|
|
|
|
const App: React.FC = () => {
|
|
const [value, setValue] = useState<UserValue[]>([]);
|
|
|
|
return (
|
|
<DebounceSelect
|
|
mode="multiple"
|
|
value={value}
|
|
placeholder="Select users"
|
|
fetchOptions={fetchUserList}
|
|
style={{ width: '100%' }}
|
|
onChange={(newValue) => {
|
|
if (Array.isArray(newValue)) {
|
|
setValue(newValue);
|
|
}
|
|
}}
|
|
/>
|
|
);
|
|
};
|
|
|
|
export default App;
|