-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathuse-mapped-with-fallback.ts
More file actions
107 lines (97 loc) · 3.26 KB
/
use-mapped-with-fallback.ts
File metadata and controls
107 lines (97 loc) · 3.26 KB
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
import { computed } from "vue";
import type { ComputedRef, Ref } from "vue";
/**
* Response returned by `useQuery()` / `useSubscription()`.
*
* `T` is the data type, the equivalent to the first argument of `UseQueryResponse` and
* `UseSubscriptionResponse`.
*/
interface Response<T> {
data: Ref<T | undefined>;
error: Ref<Error | undefined>;
}
/**
* Options for the `useMappedWithFallback` composable.
*
* The interface includes two responses and two mapping functions. The two responses are
* primary and secondary, which are typically returned by `useSubscription()` and
* `useQuery()`, respectively. The two mapping functions convert the respective response
* data into a common target type `T`. The secondary response is used as a fallback when
* the primary response data is `undefined`.
*
* @template T The target type that both mappers should produce
* @template D1 The data type of Response<D1>["data"]
* @template D2 The data type of Response<D2>["data"]
* @template R1 The primary response type, typically `UseSubscriptionResponse<D1>`
* @template R2 The fallback response type, typically `UseQueryResponse<D2>`
*
*/
export interface UseMappedWithFallbackOptions<
T,
D1,
D2,
R1 extends Response<D1>,
R2 extends Response<D2>,
> {
/** The primary response source, typically returned by `useSubscription()` */
response1: R1;
/** The fallback response source, typically returned by `useQuery()` */
response2: R2;
/** Mapping function for the primary response data. `d` is `response1.data` */
map1: (d: Ref<D1 | undefined>) => T | undefined;
/** Mapping function for the fallback response data. `d` is `response2.data` */
map2: (d: Ref<D2 | undefined>) => T | undefined;
}
/**
* Return type of `useMappedWithFallback()`.
*
* The returned object contains two reactive properties: `data` and `error`.
*/
interface UseMappedWithFallbackReturn<T> {
/**
* The reactive value mapped primarily from `response1` with fallback to `response2`.
* `undefined` if either response has an error.
*/
data: ComputedRef<T | undefined>;
/**
* The error from `response1` otherwise from `response2` else `undefined`
*/
error: ComputedRef<Error | undefined>;
}
/**
* A reactive mapped value from a primary response with secondary fallback.
*
* @param options {@link UseMappedWithFallbackOptions}
* @returns {@link UseMappedWithFallbackReturn}
* @example
* ```typescript
* const subscription = useSubscription<D1>({ query: SUBSCRIPTION_QUERY });
* const query = useQuery<D2>({ query: QUERY });
*
* const { data, error } = useMappedWithFallback({
* response1: subscription, // primary: real-time updates
* response2: query, // fallback: initial data
* map1: (d) => d.value?.ctrlState,
* map2: (d) => d.value?.ctrl.state,
* });
* ```
*/
export function useMappedWithFallback<
T,
D1,
D2,
R1 extends Response<D1>,
R2 extends Response<D2>,
>(
options: UseMappedWithFallbackOptions<T, D1, D2, R1, R2>,
): UseMappedWithFallbackReturn<T> {
const error = computed(
() => options.response1.error?.value ?? options.response2.error?.value,
);
const data = computed(() =>
error.value
? undefined
: (options.map1(options.response1.data) ?? options.map2(options.response2.data)),
);
return { data, error };
}