74 lines
1.8 KiB
TypeScript
74 lines
1.8 KiB
TypeScript
"use client";
|
|
|
|
import * as React from "react";
|
|
import Select from "react-select";
|
|
|
|
type OptionType = { label: string; value: string };
|
|
|
|
export default function AsyncSelectPaginate({
|
|
loader,
|
|
value,
|
|
onChange,
|
|
...rest
|
|
}: any) {
|
|
const [options, setOptions] = React.useState<OptionType[]>([]);
|
|
const [page, setPage] = React.useState(1);
|
|
const [hasMore, setHasMore] = React.useState(false);
|
|
const [inputValue, setInputValue] = React.useState("");
|
|
const [loading, setLoading] = React.useState(false);
|
|
|
|
React.useEffect(() => {
|
|
let mounted = true;
|
|
setLoading(true);
|
|
(async () => {
|
|
try {
|
|
const res = await loader?.(inputValue ?? "", 1);
|
|
if (!mounted) return;
|
|
const opts = (res && res.options) || [];
|
|
setOptions(opts || []);
|
|
setHasMore(Boolean(res && res.hasMore));
|
|
setPage(1);
|
|
} catch (e) {
|
|
console.error("AsyncSelectPaginate load error", e);
|
|
} finally {
|
|
if (mounted) setLoading(false);
|
|
}
|
|
})();
|
|
|
|
return () => {
|
|
mounted = false;
|
|
};
|
|
}, [inputValue, loader]);
|
|
|
|
const loadMore = async () => {
|
|
if (loading || !hasMore) return;
|
|
setLoading(true);
|
|
const next = page + 1;
|
|
try {
|
|
const res = await loader?.(inputValue ?? "", next);
|
|
const opts = (res && res.options) || [];
|
|
setOptions((s) => [...s, ...(opts || [])]);
|
|
setHasMore(Boolean(res && res.hasMore));
|
|
setPage(next);
|
|
} catch (e) {
|
|
console.error("AsyncSelectPaginate loadMore error", e);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div>
|
|
<Select
|
|
options={options}
|
|
value={value}
|
|
onChange={onChange}
|
|
onInputChange={(val) => setInputValue(val)}
|
|
onMenuScrollToBottom={loadMore}
|
|
isLoading={loading}
|
|
{...rest}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|