-
-
Notifications
You must be signed in to change notification settings - Fork 530
/
Copy pathmutate.ts
86 lines (80 loc) · 2.57 KB
/
mutate.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
import type { Client } from "openapi-fetch";
import type { MediaType, PathsWithMethod } from "openapi-typescript-helpers";
import { useCallback, useDebugValue } from "react";
import { type MutatorCallback, type MutatorOptions, useSWRConfig } from "swr";
import type { PartialDeep } from "type-fest";
import type { TypesForGetRequest } from "./types.js";
// Types are loose here to support ecosystem utilities like `_.isMatch`
export type CompareFn = (init: any, partialInit: any) => boolean;
/**
* Produces a typed wrapper for [`useSWRConfig#mutate`](https://swr.vercel.app/docs/mutation).
*
* ```ts
* import createClient from "openapi-fetch";
* import { isMatch } from "lodash";
*
* const client = createClient();
*
* const useMutate = createMutateHook(client, "<unique-key>", isMatch);
*
* const mutate = useMutate();
*
* // Revalidate all keys matching this path
* await mutate(["/pets"]);
* await mutate(["/pets"], newData);
* await mutate(["/pets"], undefined, { revalidate: true });
*
* // Revlidate all keys matching this path and this subset of options
* await mutate(
* ["/pets", { query: { limit: 10 } }],
* newData,
* { revalidate: false }
* );
* ```
*/
export function createMutateHook<Paths extends {}, IMediaType extends MediaType>(
client: Client<Paths, IMediaType>,
prefix: string,
compare: CompareFn,
) {
return function useMutate() {
const { mutate: swrMutate } = useSWRConfig();
useDebugValue(prefix);
return useCallback(
function mutate<
Path extends PathsWithMethod<Paths, "post">,
R extends TypesForGetRequest<Paths, Path>,
Init extends R["Init"],
>(
[path, init]: [Path, PartialDeep<Init>?],
data?: R["Data"] | Promise<R["Data"]> | MutatorCallback<R["Data"]>,
opts?: boolean | MutatorOptions<R["Data"]>,
) {
return swrMutate<R["Data"], R["Data"]>(
(key) => {
if (
// Must be array
!Array.isArray(key) ||
// Must have 2 or 3 elements (prefix, path, optional init)
![2, 3].includes(key.length)
) {
return false;
}
const [keyPrefix, keyPath, keyOptions] = key as unknown[];
return (
// Matching prefix
keyPrefix === prefix &&
// Matching path
keyPath === path &&
// Matching options
(init ? compare(keyOptions, init) : true)
);
},
data,
opts,
);
},
[swrMutate, prefix, compare],
);
};
}