@pixotope/query 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +10 -10
- package/.turbo/turbo-check-types.log +1 -1
- package/.turbo/turbo-test.log +11 -7
- package/CHANGELOG.md +13 -0
- package/dist/index.cjs +68 -71
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +50 -91
- package/dist/index.d.ts +50 -91
- package/dist/index.js +68 -71
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/factories.test.ts +65 -0
- package/src/factories.ts +48 -31
- package/src/proxyBinders.test.ts +42 -0
- package/src/proxyBinders.ts +8 -5
- package/src/useMutation.test.ts +106 -0
- package/src/useMutation.ts +87 -116
- package/src/useQuery.test.ts +77 -139
- package/src/useQuery.ts +5 -8
- package/src/useSubscription.test.ts +79 -0
- package/src/useSubscription.ts +23 -13
- package/src/useSubscriptionCore.ts +6 -3
- package/src/mock/useQuery.ts +0 -48
package/src/factories.ts
CHANGED
|
@@ -11,7 +11,7 @@ export type MutationInfo<TData, TError, TParams> = {
|
|
|
11
11
|
|
|
12
12
|
type FnToParamsBase = Record<string, MutationInfo<unknown, unknown, unknown>>;
|
|
13
13
|
|
|
14
|
-
export type TPromiseFunction<TResponse, TParams> = (
|
|
14
|
+
export type TPromiseFunction<TResponse, TParams, TExtraParams> = (
|
|
15
15
|
args: Omit<
|
|
16
16
|
ZMQClient.Call<
|
|
17
17
|
TResponse,
|
|
@@ -22,7 +22,9 @@ export type TPromiseFunction<TResponse, TParams> = (
|
|
|
22
22
|
}
|
|
23
23
|
>,
|
|
24
24
|
"id"
|
|
25
|
-
>
|
|
25
|
+
> & {
|
|
26
|
+
meta?: TExtraParams;
|
|
27
|
+
}
|
|
26
28
|
) => Promise<TResponse>;
|
|
27
29
|
|
|
28
30
|
const useCallMutationBase = <
|
|
@@ -30,25 +32,30 @@ const useCallMutationBase = <
|
|
|
30
32
|
TData = unknown,
|
|
31
33
|
TError = unknown,
|
|
32
34
|
TParams = unknown,
|
|
35
|
+
TMetadata = unknown,
|
|
33
36
|
>(
|
|
34
|
-
promiseFunc: TPromiseFunction<TData, TParams>,
|
|
37
|
+
promiseFunc: TPromiseFunction<TData, TParams, TMetadata>,
|
|
35
38
|
functionName: keyof TFnToParams,
|
|
36
|
-
service: string,
|
|
37
|
-
options?: Omit<
|
|
39
|
+
service: (meta?: TMetadata) => string,
|
|
40
|
+
options?: Omit<
|
|
41
|
+
MutationOptions<TData, TError, TParams, TMetadata>,
|
|
42
|
+
"mutationFn"
|
|
43
|
+
>
|
|
38
44
|
) => {
|
|
39
45
|
const timeout = options?.timeout;
|
|
40
46
|
const { ...rest } = useMemo(() => options ?? {}, [options]);
|
|
41
47
|
const mutationFn = useCallback(
|
|
42
|
-
async (parameters: TParams) => {
|
|
48
|
+
async (parameters: TParams, meta?: TMetadata) => {
|
|
43
49
|
try {
|
|
44
50
|
return await promiseFunc({
|
|
45
|
-
service,
|
|
51
|
+
service: service(meta),
|
|
46
52
|
functionName: functionName.toString(),
|
|
47
53
|
parameters,
|
|
48
54
|
options: {
|
|
49
55
|
avoidTimeout: !timeout,
|
|
50
56
|
customTimeout: timeout,
|
|
51
57
|
},
|
|
58
|
+
meta,
|
|
52
59
|
});
|
|
53
60
|
} catch (e) {
|
|
54
61
|
if (e instanceof Error) {
|
|
@@ -60,15 +67,18 @@ const useCallMutationBase = <
|
|
|
60
67
|
[functionName, service, timeout]
|
|
61
68
|
);
|
|
62
69
|
|
|
63
|
-
return useMutation<TData, TError, TParams>({
|
|
70
|
+
return useMutation<TData, TError, TParams, TMetadata>({
|
|
64
71
|
mutationFn,
|
|
65
72
|
...rest,
|
|
66
73
|
});
|
|
67
74
|
};
|
|
68
75
|
|
|
69
|
-
export function buildMutationHook<
|
|
70
|
-
|
|
71
|
-
|
|
76
|
+
export function buildMutationHook<
|
|
77
|
+
TFnToParams extends FnToParamsBase,
|
|
78
|
+
TMetadata = unknown,
|
|
79
|
+
>(
|
|
80
|
+
promiseFunc: TPromiseFunction<unknown, unknown, unknown>,
|
|
81
|
+
genTargetService: (meta?: TMetadata) => string
|
|
72
82
|
) {
|
|
73
83
|
return function useCallMutationBuilder<TFn extends keyof TFnToParams>(
|
|
74
84
|
func: TFn,
|
|
@@ -77,18 +87,18 @@ export function buildMutationHook<TFnToParams extends FnToParamsBase>(
|
|
|
77
87
|
TFnToParams,
|
|
78
88
|
TFnToParams[TFn]["data"],
|
|
79
89
|
TFnToParams[TFn]["error"],
|
|
80
|
-
TFnToParams[TFn]["params"]
|
|
90
|
+
TFnToParams[TFn]["params"],
|
|
91
|
+
TMetadata
|
|
81
92
|
>
|
|
82
|
-
>[3]
|
|
93
|
+
>[3]
|
|
83
94
|
) {
|
|
84
|
-
const targetService = genTargetService(options?.machine);
|
|
85
|
-
|
|
86
95
|
return useCallMutationBase<
|
|
87
96
|
TFnToParams,
|
|
88
97
|
TFnToParams[TFn]["data"],
|
|
89
98
|
TFnToParams[TFn]["error"],
|
|
90
|
-
TFnToParams[TFn]["params"]
|
|
91
|
-
|
|
99
|
+
TFnToParams[TFn]["params"],
|
|
100
|
+
TMetadata
|
|
101
|
+
>(promiseFunc, func, genTargetService, options);
|
|
92
102
|
};
|
|
93
103
|
}
|
|
94
104
|
|
|
@@ -98,26 +108,30 @@ const useCallQueryBase = <
|
|
|
98
108
|
TError = unknown,
|
|
99
109
|
TParams = unknown,
|
|
100
110
|
TSelect = TData,
|
|
111
|
+
TMetadata = unknown,
|
|
101
112
|
>(
|
|
102
|
-
promiseFunction: TPromiseFunction<TData, TParams>,
|
|
113
|
+
promiseFunction: TPromiseFunction<TData, TParams, TMetadata>,
|
|
103
114
|
functionName: keyof TFnToParams,
|
|
104
115
|
params: TParams,
|
|
105
|
-
service: string,
|
|
106
|
-
options?: Omit<QueryOptions<TData, TError, TParams, TSelect>, "queryFn">
|
|
116
|
+
service: (meta?: TMetadata) => string,
|
|
117
|
+
options?: Omit<QueryOptions<TData, TError, TParams, TSelect>, "queryFn"> & {
|
|
118
|
+
meta?: TMetadata;
|
|
119
|
+
}
|
|
107
120
|
) => {
|
|
108
121
|
const timeout = options?.timeout;
|
|
109
122
|
const { ...rest } = useMemo(() => options ?? {}, [options]);
|
|
110
123
|
const queryFn = useCallback(
|
|
111
|
-
async (refetchParams: TParams | void
|
|
124
|
+
async (refetchParams: TParams | void) => {
|
|
112
125
|
try {
|
|
113
126
|
return await promiseFunction({
|
|
114
|
-
service:
|
|
127
|
+
service: service(options?.meta),
|
|
115
128
|
functionName: functionName.toString(),
|
|
116
129
|
parameters: refetchParams ?? params,
|
|
117
130
|
options: {
|
|
118
131
|
avoidTimeout: !timeout,
|
|
119
132
|
customTimeout: timeout,
|
|
120
133
|
},
|
|
134
|
+
meta: options?.meta,
|
|
121
135
|
});
|
|
122
136
|
} catch (e) {
|
|
123
137
|
if (e instanceof Error) {
|
|
@@ -135,9 +149,12 @@ const useCallQueryBase = <
|
|
|
135
149
|
});
|
|
136
150
|
};
|
|
137
151
|
|
|
138
|
-
export function buildQueryHook<
|
|
139
|
-
|
|
140
|
-
|
|
152
|
+
export function buildQueryHook<
|
|
153
|
+
TFnToParams extends FnToParamsBase,
|
|
154
|
+
TMetadata = unknown,
|
|
155
|
+
>(
|
|
156
|
+
promiseFunction: TPromiseFunction<unknown, unknown, unknown>,
|
|
157
|
+
getTargetService: (meta?: TMetadata) => string
|
|
141
158
|
) {
|
|
142
159
|
return function useCallQueryBuilder<
|
|
143
160
|
TFn extends keyof TFnToParams,
|
|
@@ -151,18 +168,18 @@ export function buildQueryHook<TFnToParams extends FnToParamsBase>(
|
|
|
151
168
|
TFnToParams[TFn]["data"],
|
|
152
169
|
TFnToParams[TFn]["error"],
|
|
153
170
|
TFnToParams[TFn]["params"],
|
|
154
|
-
TSelect
|
|
171
|
+
TSelect,
|
|
172
|
+
TMetadata
|
|
155
173
|
>
|
|
156
|
-
>[4] & {
|
|
174
|
+
>[4] & { meta?: TMetadata }
|
|
157
175
|
) {
|
|
158
|
-
const targetService = getTargetService(options?.machine);
|
|
159
|
-
|
|
160
176
|
return useCallQueryBase<
|
|
161
177
|
TFnToParams,
|
|
162
178
|
TFnToParams[TFn]["data"],
|
|
163
179
|
TFnToParams[TFn]["error"],
|
|
164
180
|
TFnToParams[TFn]["params"],
|
|
165
|
-
TSelect
|
|
166
|
-
|
|
181
|
+
TSelect,
|
|
182
|
+
TMetadata
|
|
183
|
+
>(promiseFunction, func, params, getTargetService, options);
|
|
167
184
|
};
|
|
168
185
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { vi, describe, expect, it, beforeEach } from "vitest";
|
|
2
|
+
import { bindClientSocketProxy, bindPromiseFunction } from "./proxyBinders";
|
|
3
|
+
import type { ZMQProxyWSClient } from "@pixotope/zmq-client";
|
|
4
|
+
|
|
5
|
+
describe("proxyBinders", () => {
|
|
6
|
+
let clientSocketProxy: ZMQProxyWSClient;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
clientSocketProxy = {
|
|
10
|
+
callAsync: vi.fn().mockResolvedValue({ message: { Result: "test" } }),
|
|
11
|
+
mirror: vi.fn().mockReturnValue([vi.fn(), "testID"]),
|
|
12
|
+
} as unknown as ZMQProxyWSClient;
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe("bindClientSocketProxy", () => {
|
|
16
|
+
it("should correctly bind mirror function", () => {
|
|
17
|
+
bindClientSocketProxy(clientSocketProxy)({
|
|
18
|
+
name: "test",
|
|
19
|
+
service: "test",
|
|
20
|
+
callback: vi.fn(),
|
|
21
|
+
maxDownwardDepth: 1000,
|
|
22
|
+
});
|
|
23
|
+
expect(clientSocketProxy.mirror).toHaveBeenCalledWith(
|
|
24
|
+
expect.objectContaining({
|
|
25
|
+
name: "test",
|
|
26
|
+
service: "test",
|
|
27
|
+
maxDownwardDepth: 1000,
|
|
28
|
+
})
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe("bindPromiseFunction", () => {
|
|
34
|
+
it("should correctly bind callAsync function", () => {
|
|
35
|
+
bindPromiseFunction(clientSocketProxy)({
|
|
36
|
+
functionName: "test",
|
|
37
|
+
parameters: { test: "test" },
|
|
38
|
+
service: "test",
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
});
|
package/src/proxyBinders.ts
CHANGED
|
@@ -2,11 +2,12 @@ import type { ZMQProxyWSClient } from "@pixotope/zmq-client";
|
|
|
2
2
|
import type { TPromiseFunction } from "./factories";
|
|
3
3
|
import type { buildServiceSubscriptionHook } from "./useSubscription";
|
|
4
4
|
|
|
5
|
+
export type TProxySubscriptionHandlerParams<TMetadata extends {} = {}> =
|
|
6
|
+
Parameters<Parameters<typeof buildServiceSubscriptionHook<TMetadata>>[0]>[0];
|
|
7
|
+
|
|
5
8
|
export function bindClientSocketProxy(socketClientProxy: ZMQProxyWSClient) {
|
|
6
|
-
return (
|
|
7
|
-
|
|
8
|
-
) => {
|
|
9
|
-
const { callback: cbHook, ...rest } = args[0];
|
|
9
|
+
return (args: TProxySubscriptionHandlerParams) => {
|
|
10
|
+
const { callback: cbHook, ...rest } = args;
|
|
10
11
|
return socketClientProxy.mirror({
|
|
11
12
|
...rest,
|
|
12
13
|
callback: (message) => {
|
|
@@ -17,7 +18,9 @@ export function bindClientSocketProxy(socketClientProxy: ZMQProxyWSClient) {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export function bindPromiseFunction(clientSocketProxy: ZMQProxyWSClient) {
|
|
20
|
-
return async (
|
|
21
|
+
return async (
|
|
22
|
+
...args: Parameters<TPromiseFunction<unknown, unknown, unknown>>
|
|
23
|
+
) => {
|
|
21
24
|
const res = await clientSocketProxy.callAsync(...args);
|
|
22
25
|
return res.message.Result;
|
|
23
26
|
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { useMutation } from "./useMutation";
|
|
3
|
+
import { act, renderHook, waitFor } from "@testing-library/react";
|
|
4
|
+
|
|
5
|
+
describe("useMutation", () => {
|
|
6
|
+
it("should be in idle state on mount", () => {
|
|
7
|
+
const { result } = renderHook(() => useMutation({ mutationFn: vi.fn() }));
|
|
8
|
+
expect(result.current.status).toBe("idle");
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it("should be in loading state after mutationFn is called", async () => {
|
|
12
|
+
const { result } = renderHook(() => useMutation({ mutationFn: vi.fn() }));
|
|
13
|
+
act(() => result.current.mutate({}));
|
|
14
|
+
await waitFor(() => expect(result.current.status).toBe("loading"));
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("should be in success state after mutationFn resolves", async () => {
|
|
18
|
+
const { result } = renderHook(() =>
|
|
19
|
+
useMutation({ mutationFn: vi.fn().mockResolvedValue("test") })
|
|
20
|
+
);
|
|
21
|
+
act(() => result.current.mutate({}));
|
|
22
|
+
await waitFor(() => expect(result.current.status).toBe("success"));
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("should be in error state after mutationFn rejects", async () => {
|
|
26
|
+
const { result } = renderHook(() =>
|
|
27
|
+
useMutation({ mutationFn: vi.fn().mockRejectedValue("test") })
|
|
28
|
+
);
|
|
29
|
+
act(() => result.current.mutate({}));
|
|
30
|
+
await waitFor(() => expect(result.current.status).toBe("error"));
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should call onSuccess callback after mutationFn resolves", async () => {
|
|
34
|
+
const onSuccess = vi.fn();
|
|
35
|
+
const { result } = renderHook(() =>
|
|
36
|
+
useMutation({ mutationFn: vi.fn().mockResolvedValue("test"), onSuccess })
|
|
37
|
+
);
|
|
38
|
+
act(() => result.current.mutate({}, { onSuccess }));
|
|
39
|
+
await waitFor(() => expect(onSuccess).toBeCalledTimes(2));
|
|
40
|
+
expect(onSuccess).toBeCalledWith("test", {}, undefined);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("should call onError callback after mutationFn rejects", async () => {
|
|
44
|
+
const onError = vi.fn();
|
|
45
|
+
const { result } = renderHook(() =>
|
|
46
|
+
useMutation({ mutationFn: vi.fn().mockRejectedValue("test"), onError })
|
|
47
|
+
);
|
|
48
|
+
act(() => result.current.mutate({}, { onError }));
|
|
49
|
+
await waitFor(() => expect(onError).toBeCalledTimes(2));
|
|
50
|
+
expect(onError).toBeCalledWith("test", {}, undefined);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("should call onSettled callback after mutationFn resolves", async () => {
|
|
54
|
+
const onSettled = vi.fn();
|
|
55
|
+
const { result } = renderHook(() =>
|
|
56
|
+
useMutation({
|
|
57
|
+
mutationFn: vi.fn().mockResolvedValue("test"),
|
|
58
|
+
onSettled,
|
|
59
|
+
})
|
|
60
|
+
);
|
|
61
|
+
act(() => result.current.mutate({}, { onSettled }));
|
|
62
|
+
await waitFor(() => expect(onSettled).toBeCalledTimes(2));
|
|
63
|
+
expect(onSettled).toBeCalledWith("test", null, {}, undefined);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("should call onSettled callback after mutationFn rejects", async () => {
|
|
67
|
+
const onSettled = vi.fn();
|
|
68
|
+
const { result } = renderHook(() =>
|
|
69
|
+
useMutation({ mutationFn: vi.fn().mockRejectedValue("test"), onSettled })
|
|
70
|
+
);
|
|
71
|
+
act(() => result.current.mutate({}, { onSettled }));
|
|
72
|
+
await waitFor(() => expect(onSettled).toBeCalledTimes(2));
|
|
73
|
+
expect(onSettled).toBeCalledWith(undefined, "test", {}, undefined);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("should pass correct params mutationFn and callbacks to mutationFn", async () => {
|
|
77
|
+
const mutationFn = vi.fn();
|
|
78
|
+
const params = { foo: "bar" };
|
|
79
|
+
const { result } = renderHook(() => useMutation({ mutationFn }));
|
|
80
|
+
act(() => result.current.mutate(params));
|
|
81
|
+
await waitFor(() => expect(mutationFn).toBeCalledTimes(1));
|
|
82
|
+
expect(mutationFn).toBeCalledWith(params, undefined);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("should pass meta to mutationFn", async () => {
|
|
86
|
+
const mutationFn = vi.fn();
|
|
87
|
+
const { result } = renderHook(() => useMutation({ mutationFn }));
|
|
88
|
+
act(() => result.current.mutate({}, { meta: { foo: "bar" } }));
|
|
89
|
+
await waitFor(() => expect(mutationFn).toBeCalledTimes(1));
|
|
90
|
+
expect(mutationFn).toBeCalledWith({}, { foo: "bar" });
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("should call prepareParams", async () => {
|
|
94
|
+
const prepareParams = vi.fn();
|
|
95
|
+
const { result } = renderHook(() =>
|
|
96
|
+
useMutation({
|
|
97
|
+
mutationFn: vi.fn(),
|
|
98
|
+
prepareParams,
|
|
99
|
+
params: { baz: "baz" },
|
|
100
|
+
})
|
|
101
|
+
);
|
|
102
|
+
act(() => result.current.mutate({}, { meta: { foo: "bar" } }));
|
|
103
|
+
await waitFor(() => expect(prepareParams).toBeCalledTimes(1));
|
|
104
|
+
expect(prepareParams).toBeCalledWith({}, { baz: "baz" }, { foo: "bar" });
|
|
105
|
+
});
|
|
106
|
+
});
|
package/src/useMutation.ts
CHANGED
|
@@ -1,30 +1,42 @@
|
|
|
1
1
|
import { useCallback, useRef, useState } from "react";
|
|
2
2
|
|
|
3
|
-
export type MutationFunction<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
export type MutationFunction<
|
|
4
|
+
TData = unknown,
|
|
5
|
+
TParams = unknown,
|
|
6
|
+
TMetadata = unknown,
|
|
7
|
+
> = (params: TParams, meta?: TMetadata) => Promise<TData>;
|
|
7
8
|
|
|
8
9
|
export interface MutationOptions<
|
|
9
10
|
TData = unknown,
|
|
10
11
|
TError = unknown,
|
|
11
12
|
TParams = void,
|
|
13
|
+
TMetadata = unknown,
|
|
12
14
|
> {
|
|
13
|
-
mutationFn: MutationFunction<TData, TParams>;
|
|
15
|
+
mutationFn: MutationFunction<TData, TParams, TMetadata>;
|
|
14
16
|
params?: TParams;
|
|
15
|
-
onSuccess?: (
|
|
17
|
+
onSuccess?: (
|
|
18
|
+
data: TData,
|
|
19
|
+
variables: TParams,
|
|
20
|
+
meta?: TMetadata
|
|
21
|
+
) => Promise<unknown> | unknown;
|
|
16
22
|
onError?: (
|
|
17
23
|
error: TMutationError<TError>,
|
|
18
|
-
variables: TParams
|
|
24
|
+
variables: TParams,
|
|
25
|
+
meta?: TMetadata
|
|
19
26
|
) => Promise<unknown> | unknown;
|
|
20
27
|
onSettled?: (
|
|
21
28
|
data: TData | undefined,
|
|
22
29
|
error: TMutationError<TError> | null,
|
|
23
|
-
variables: TParams
|
|
30
|
+
variables: TParams,
|
|
31
|
+
meta?: TMetadata
|
|
24
32
|
) => Promise<unknown> | unknown;
|
|
25
33
|
compareOrThrow?: (result: TData | TError) => boolean;
|
|
26
34
|
timeout?: number;
|
|
27
|
-
prepareParams?: (
|
|
35
|
+
prepareParams?: (
|
|
36
|
+
variables: TParams,
|
|
37
|
+
defaults?: TParams,
|
|
38
|
+
meta?: TMetadata
|
|
39
|
+
) => TParams;
|
|
28
40
|
}
|
|
29
41
|
|
|
30
42
|
type TMutationError<TError> = TError | Error | null;
|
|
@@ -33,13 +45,19 @@ export interface MutateOptions<
|
|
|
33
45
|
TData = unknown,
|
|
34
46
|
TError = unknown,
|
|
35
47
|
TParams = void,
|
|
48
|
+
TMetadata = unknown,
|
|
36
49
|
> {
|
|
37
|
-
onSuccess?: (data: TData, variables: TParams) => void;
|
|
38
|
-
onError?: (
|
|
50
|
+
onSuccess?: (data: TData, variables: TParams, meta?: TMetadata) => void;
|
|
51
|
+
onError?: (
|
|
52
|
+
error: TMutationError<TError>,
|
|
53
|
+
variables: TParams,
|
|
54
|
+
meta?: TMetadata
|
|
55
|
+
) => void;
|
|
39
56
|
onSettled?: (
|
|
40
57
|
data: TData | undefined,
|
|
41
58
|
error: TMutationError<TError> | null,
|
|
42
|
-
variables: TParams
|
|
59
|
+
variables: TParams,
|
|
60
|
+
meta?: TMetadata
|
|
43
61
|
) => void;
|
|
44
62
|
}
|
|
45
63
|
|
|
@@ -61,58 +79,13 @@ export class ComparisonFailError<TPayload = unknown> extends Error {
|
|
|
61
79
|
}
|
|
62
80
|
}
|
|
63
81
|
|
|
64
|
-
/**
|
|
65
|
-
* `useMutation` is a hook that allows you to perform a mutation/push updates to the services while
|
|
66
|
-
* providing extensive API to tap into the status and results of the mutation.
|
|
67
|
-
* @param mutationFn The mutation function to be called when the `mutate` function is called.
|
|
68
|
-
* `useMutation` does not care about what the function does, all it needs it a function that returns
|
|
69
|
-
* a promise.
|
|
70
|
-
* @param options Options to configure the mutation.
|
|
71
|
-
* @returns An object with the following properties:
|
|
72
|
-
* - `mutateAsync`: A function that takes in the variables and returns a promise that resolves to the
|
|
73
|
-
* result of the mutation. This function can be awaited to get the result of the mutation. Callbacks can
|
|
74
|
-
* be passed to this function to be called when the mutation is successful or when it fails. It's good idea
|
|
75
|
-
* to wrap this function in a try/catch block.
|
|
76
|
-
* - `mutate`: A function that takes in the variables and returns nothing. This function does not
|
|
77
|
-
* return a promise. Callbacks can be passed to this function to be called when the mutation is
|
|
78
|
-
* successful or when it fails.
|
|
79
|
-
* - `status`: The status of the mutation. Can be one of `idle`, `loading`, `success` or `error`.
|
|
80
|
-
* - `result`: The result of the mutation. This is undefined until the mutation is successful.
|
|
81
|
-
* - `reset`: A function that resets the mutation to the initial state.
|
|
82
|
-
* - `error`: The error object if the mutation has failed.
|
|
83
|
-
* @example
|
|
84
|
-
* ```typescript
|
|
85
|
-
* const mutationRes = useMutation({
|
|
86
|
-
* mutationFn: (variables) => {
|
|
87
|
-
* return ZMQHelper.callSomethingAsync(variables)
|
|
88
|
-
* },
|
|
89
|
-
* onSuccess: (data) => {
|
|
90
|
-
* // do something with the result
|
|
91
|
-
* },
|
|
92
|
-
* onError: (error) => {
|
|
93
|
-
* // do something with the error
|
|
94
|
-
* },
|
|
95
|
-
* onSettled: (data, error) => {
|
|
96
|
-
* // do something when the mutation is done
|
|
97
|
-
* }});
|
|
98
|
-
*
|
|
99
|
-
* // call the mutation
|
|
100
|
-
* const res = await mutationRes.mutateAsync({ foo: "bar" });
|
|
101
|
-
* // OR
|
|
102
|
-
* mutationRes.mutate({ foo: "bar" }, {
|
|
103
|
-
* onSuccess: (data) => {
|
|
104
|
-
* // do something with the result
|
|
105
|
-
* // for example show a toast message
|
|
106
|
-
* // stop local loading state
|
|
107
|
-
* });
|
|
108
|
-
* ```
|
|
109
|
-
*/
|
|
110
82
|
export const useMutation = <
|
|
111
83
|
TData = unknown,
|
|
112
84
|
TError = TMutationError<unknown>,
|
|
113
85
|
TParams = void,
|
|
86
|
+
TMetadata = unknown,
|
|
114
87
|
>(
|
|
115
|
-
options: MutationOptions<TData, TError, TParams>
|
|
88
|
+
options: MutationOptions<TData, TError, TParams, TMetadata>
|
|
116
89
|
) => {
|
|
117
90
|
const {
|
|
118
91
|
mutationFn,
|
|
@@ -142,79 +115,77 @@ export const useMutation = <
|
|
|
142
115
|
const internalCallbackRef = useRef(internalCallbackRefistry);
|
|
143
116
|
internalCallbackRef.current = internalCallbackRefistry;
|
|
144
117
|
|
|
145
|
-
const mutateAsync = useCallback(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
internalCallbackRef.current.prepareParams
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
118
|
+
const mutateAsync = useCallback(async (params: TParams, meta?: TMetadata) => {
|
|
119
|
+
setStatus("loading");
|
|
120
|
+
setError(null);
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
const promises: RacePromises<TData>[] = [
|
|
124
|
+
mutationFnRef.current(
|
|
125
|
+
internalCallbackRef.current.prepareParams
|
|
126
|
+
? internalCallbackRef.current.prepareParams(
|
|
127
|
+
params,
|
|
128
|
+
internalOptionsRef.current.params,
|
|
129
|
+
meta
|
|
130
|
+
)
|
|
131
|
+
: params,
|
|
132
|
+
meta
|
|
133
|
+
),
|
|
134
|
+
...(internalOptionsRef.current.timeout
|
|
135
|
+
? [
|
|
136
|
+
new Promise<RaceErrorReason>((resolve) =>
|
|
137
|
+
setTimeout(
|
|
138
|
+
() => resolve("timeout"),
|
|
139
|
+
internalOptionsRef.current.timeout
|
|
157
140
|
)
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
),
|
|
161
|
-
|
|
162
|
-
? [
|
|
163
|
-
new Promise<RaceErrorReason>((resolve) =>
|
|
164
|
-
setTimeout(
|
|
165
|
-
() => resolve("timeout"),
|
|
166
|
-
internalOptionsRef.current.timeout
|
|
167
|
-
)
|
|
168
|
-
),
|
|
169
|
-
]
|
|
170
|
-
: []),
|
|
171
|
-
];
|
|
172
|
-
|
|
173
|
-
const res = await Promise.race(promises);
|
|
174
|
-
|
|
175
|
-
if (res === "timeout") {
|
|
176
|
-
throw new Error("timeout");
|
|
177
|
-
}
|
|
141
|
+
),
|
|
142
|
+
]
|
|
143
|
+
: []),
|
|
144
|
+
];
|
|
178
145
|
|
|
179
|
-
|
|
180
|
-
throw new ComparisonFailError(JSON.stringify(res), res);
|
|
181
|
-
}
|
|
146
|
+
const res = await Promise.race(promises);
|
|
182
147
|
|
|
183
|
-
|
|
184
|
-
|
|
148
|
+
if (res === "timeout") {
|
|
149
|
+
throw new Error("timeout");
|
|
150
|
+
}
|
|
185
151
|
|
|
186
|
-
|
|
187
|
-
|
|
152
|
+
if (internalCallbackRef.current.compareOrThrow?.(res)) {
|
|
153
|
+
throw new ComparisonFailError(JSON.stringify(res), res);
|
|
154
|
+
}
|
|
188
155
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
setError(error);
|
|
192
|
-
setStatus("error");
|
|
156
|
+
setResult(res);
|
|
157
|
+
setStatus("success");
|
|
193
158
|
|
|
194
|
-
|
|
195
|
-
|
|
159
|
+
internalCallbackRef.current.onSuccess?.(res, params, meta);
|
|
160
|
+
internalCallbackRef.current.onSettled?.(res, null, params, meta);
|
|
196
161
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
162
|
+
return res;
|
|
163
|
+
} catch (error: any) {
|
|
164
|
+
setError(error);
|
|
165
|
+
setStatus("error");
|
|
166
|
+
|
|
167
|
+
internalCallbackRef.current.onError?.(error, params, meta);
|
|
168
|
+
internalCallbackRef.current.onSettled?.(undefined, error, params, meta);
|
|
169
|
+
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
}, []);
|
|
202
173
|
|
|
203
174
|
const mutate = useCallback(
|
|
204
175
|
(
|
|
205
176
|
variables: TParams,
|
|
206
|
-
options?: MutateOptions<TData, TError, TParams> & {
|
|
207
|
-
|
|
177
|
+
options?: MutateOptions<TData, TError, TParams, TMetadata> & {
|
|
178
|
+
meta?: TMetadata;
|
|
208
179
|
}
|
|
209
180
|
) => {
|
|
210
|
-
mutateAsync(variables, options?.
|
|
181
|
+
mutateAsync(variables, options?.meta).then(
|
|
211
182
|
(data) => {
|
|
212
|
-
options?.onSuccess?.(data, variables);
|
|
213
|
-
options?.onSettled?.(data, null, variables);
|
|
183
|
+
options?.onSuccess?.(data, variables, options?.meta);
|
|
184
|
+
options?.onSettled?.(data, null, variables, options?.meta);
|
|
214
185
|
},
|
|
215
186
|
(error) => {
|
|
216
|
-
options?.onError?.(error, variables);
|
|
217
|
-
options?.onSettled?.(undefined, error, variables);
|
|
187
|
+
options?.onError?.(error, variables, options?.meta);
|
|
188
|
+
options?.onSettled?.(undefined, error, variables, options?.meta);
|
|
218
189
|
}
|
|
219
190
|
);
|
|
220
191
|
},
|