2017-03-22 14:21:24 +08:00
|
|
|
---
|
|
|
|
order: 12
|
|
|
|
title:
|
|
|
|
zh-CN: 搜索用户
|
|
|
|
en-US: Search and Select Users
|
|
|
|
---
|
|
|
|
|
|
|
|
## zh-CN
|
|
|
|
|
2019-05-09 20:43:12 +08:00
|
|
|
一个带有远程搜索,防抖控制,请求时序控制,加载状态的多选示例。
|
2017-03-22 14:21:24 +08:00
|
|
|
|
|
|
|
## en-US
|
|
|
|
|
|
|
|
A complete multiple select sample with remote search, debounce fetch, ajax callback order flow, and loading state.
|
|
|
|
|
2021-02-22 15:55:17 +08:00
|
|
|
```tsx
|
2017-03-22 14:21:24 +08:00
|
|
|
import { Select, Spin } from 'antd';
|
2022-05-07 14:31:54 +08:00
|
|
|
import type { SelectProps } from 'antd/es/select';
|
2018-03-08 13:36:12 +08:00
|
|
|
import debounce from 'lodash/debounce';
|
2022-05-25 21:10:07 +08:00
|
|
|
import React, { useMemo, useRef, useState } from 'react';
|
2018-06-27 15:55:04 +08:00
|
|
|
|
2021-02-22 15:55:17 +08:00
|
|
|
export interface DebounceSelectProps<ValueType = any>
|
2022-05-25 21:10:07 +08:00
|
|
|
extends Omit<SelectProps<ValueType | ValueType[]>, 'options' | 'children'> {
|
2021-02-22 15:55:17 +08:00
|
|
|
fetchOptions: (search: string) => Promise<ValueType[]>;
|
|
|
|
debounceTimeout?: number;
|
|
|
|
}
|
|
|
|
|
|
|
|
function DebounceSelect<
|
2021-12-21 21:38:11 +08:00
|
|
|
ValueType extends { key?: string; label: React.ReactNode; value: string | number } = any,
|
2022-05-25 21:10:07 +08:00
|
|
|
>({ fetchOptions, debounceTimeout = 800, ...props }: DebounceSelectProps<ValueType>) {
|
2022-05-19 09:46:26 +08:00
|
|
|
const [fetching, setFetching] = useState(false);
|
|
|
|
const [options, setOptions] = useState<ValueType[]>([]);
|
|
|
|
const fetchRef = useRef(0);
|
2021-02-22 15:55:17 +08:00
|
|
|
|
2022-05-19 09:46:26 +08:00
|
|
|
const debounceFetcher = useMemo(() => {
|
2021-02-22 15:55:17 +08:00
|
|
|
const loadOptions = (value: string) => {
|
|
|
|
fetchRef.current += 1;
|
|
|
|
const fetchId = fetchRef.current;
|
|
|
|
setOptions([]);
|
|
|
|
setFetching(true);
|
|
|
|
|
|
|
|
fetchOptions(value).then(newOptions => {
|
|
|
|
if (fetchId !== fetchRef.current) {
|
2019-05-07 14:57:32 +08:00
|
|
|
// for fetch callback order
|
2017-03-22 14:21:24 +08:00
|
|
|
return;
|
|
|
|
}
|
2021-02-22 15:55:17 +08:00
|
|
|
|
|
|
|
setOptions(newOptions);
|
|
|
|
setFetching(false);
|
2017-03-22 14:21:24 +08:00
|
|
|
});
|
2021-02-22 15:55:17 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
return debounce(loadOptions, debounceTimeout);
|
|
|
|
}, [fetchOptions, debounceTimeout]);
|
|
|
|
|
|
|
|
return (
|
2022-05-25 21:10:07 +08:00
|
|
|
<Select
|
2021-02-22 15:55:17 +08:00
|
|
|
labelInValue
|
|
|
|
filterOption={false}
|
|
|
|
onSearch={debounceFetcher}
|
|
|
|
notFoundContent={fetching ? <Spin size="small" /> : null}
|
|
|
|
{...props}
|
|
|
|
options={options}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Usage of DebounceSelect
|
|
|
|
interface UserValue {
|
|
|
|
label: string;
|
|
|
|
value: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function fetchUserList(username: string): Promise<UserValue[]> {
|
|
|
|
console.log('fetching user', username);
|
|
|
|
|
|
|
|
return fetch('https://randomuser.me/api/?results=5')
|
|
|
|
.then(response => response.json())
|
|
|
|
.then(body =>
|
|
|
|
body.results.map(
|
|
|
|
(user: { name: { first: string; last: string }; login: { username: string } }) => ({
|
|
|
|
label: `${user.name.first} ${user.name.last}`,
|
|
|
|
value: user.login.username,
|
|
|
|
}),
|
|
|
|
),
|
2017-03-22 14:21:24 +08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-05-19 09:46:26 +08:00
|
|
|
const App: React.FC = () => {
|
|
|
|
const [value, setValue] = useState<UserValue[]>([]);
|
2021-02-22 15:55:17 +08:00
|
|
|
|
|
|
|
return (
|
|
|
|
<DebounceSelect
|
|
|
|
mode="multiple"
|
|
|
|
value={value}
|
|
|
|
placeholder="Select users"
|
|
|
|
fetchOptions={fetchUserList}
|
|
|
|
onChange={newValue => {
|
2022-05-25 21:10:07 +08:00
|
|
|
setValue(newValue as UserValue[]);
|
2021-02-22 15:55:17 +08:00
|
|
|
}}
|
|
|
|
style={{ width: '100%' }}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2022-05-19 09:46:26 +08:00
|
|
|
export default App;
|
2019-05-07 14:57:32 +08:00
|
|
|
```
|