/
useAsyncOptions.ts
109 lines (101 loc) · 2.49 KB
/
useAsyncOptions.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import { debounce } from "lodash";
import { useMemo, useState } from "react";
import { useDispatch } from "react-redux";
interface IUseAsyncOptionsArgs {
debounceInterval?: number;
}
/**
* Hook to implement async autocompletes with ease and typesafety.
*
* See `DiagnosisSelectFormField` for usage.
*
* **Example usage:**
* ```jsx
* const { fetchOptions, isLoading, options } = useAsyncOptions<Model>("id");
*
* return (
* <AutocompleteMultiselect
* ...
* options={options(props.value)}
* isLoading={isLoading}
* onQuery={(query) => fetchOptions(action({ query }))}
* optionValue={(option) => option}
* ...
* />
* );
* ```
*/
export function useAsyncOptions<T extends Record<string, unknown>>(
uniqueKey: keyof T,
args?: IUseAsyncOptionsArgs
) {
const dispatch = useDispatch<any>();
const [queryOptions, setQueryOptions] = useState<T[]>([]);
const [isLoading, setIsLoading] = useState(false);
const fetchOptions = useMemo(
() =>
debounce(async (action: any) => {
setIsLoading(true);
const res = await dispatch(action);
if (res?.data) setQueryOptions(res.data as T[]);
setIsLoading(false);
}, args?.debounceInterval ?? 300),
[dispatch, args?.debounceInterval]
);
const mergeValueWithQueryOptions = (selected?: T[]) => {
if (!selected?.length) return queryOptions;
return [
...queryOptions,
...selected.filter(
(obj) => !queryOptions.some((s) => s[uniqueKey] === obj[uniqueKey])
),
];
};
return {
/**
* Merges query options and selected options.
*
* **Example usage:**
* ```jsx
* const { isLoading } = useAsyncOptions<Model>("id");
*
* <AutocompleteMultiselect
* ...
* isLoading={isLoading}
* ...
* />
* ```
*/
fetchOptions,
/**
* Merges query options and selected options.
*
* **Example usage:**
* ```jsx
* const { options } = useAsyncOptions<Model>("id");
*
* <AutocompleteMultiselect
* ...
* onQuery={(query) => fetchOptions(action({ query }))}
* ...
* />
* ```
*/
isLoading,
/**
* Merges query options and selected options.
*
* **Example usage:**
* ```jsx
* const { options } = useAsyncOptions<Model>("id");
*
* <AutocompleteMultiselect
* ...
* options={options(props.value)}
* ...
* />
* ```
*/
options: mergeValueWithQueryOptions,
};
}