@pixotope/query 0.5.0 → 0.7.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 +9 -7
- package/CHANGELOG.md +14 -0
- package/dist/index.cjs +81 -93
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +42 -88
- package/dist/index.d.ts +42 -88
- package/dist/index.js +81 -93
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/factories.test.ts +65 -0
- package/src/factories.ts +61 -53
- package/src/proxyBinders.ts +3 -1
- package/src/useMutation.test.ts +106 -0
- package/src/useMutation.ts +104 -125
- package/src/useQuery.test.ts +77 -139
- package/src/useQuery.ts +15 -16
- 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,48 +32,53 @@ 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<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
service: (meta?: TMetadata) => string,
|
|
40
|
+
options?: Omit<
|
|
41
|
+
MutationOptions<TData, TError, TParams, TMetadata>,
|
|
42
|
+
"mutationFn"
|
|
43
|
+
>
|
|
41
44
|
) => {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
customTimeout = undefined,
|
|
45
|
-
...rest
|
|
46
|
-
} = useMemo(() => options ?? {}, [options]);
|
|
45
|
+
const timeout = options?.timeout;
|
|
46
|
+
const { ...rest } = useMemo(() => options ?? {}, [options]);
|
|
47
47
|
const mutationFn = useCallback(
|
|
48
|
-
async (parameters: TParams) => {
|
|
48
|
+
async (parameters: TParams, meta?: TMetadata) => {
|
|
49
49
|
try {
|
|
50
50
|
return await promiseFunc({
|
|
51
|
-
service,
|
|
51
|
+
service: service(meta),
|
|
52
52
|
functionName: functionName.toString(),
|
|
53
53
|
parameters,
|
|
54
54
|
options: {
|
|
55
|
-
avoidTimeout,
|
|
56
|
-
customTimeout,
|
|
55
|
+
avoidTimeout: !timeout,
|
|
56
|
+
customTimeout: timeout,
|
|
57
57
|
},
|
|
58
|
+
meta,
|
|
58
59
|
});
|
|
59
|
-
} catch (e
|
|
60
|
-
|
|
60
|
+
} catch (e) {
|
|
61
|
+
if (e instanceof Error) {
|
|
62
|
+
throw e;
|
|
63
|
+
}
|
|
64
|
+
throw new Error(e as string);
|
|
61
65
|
}
|
|
62
66
|
},
|
|
63
|
-
[
|
|
67
|
+
[functionName, service, timeout]
|
|
64
68
|
);
|
|
65
69
|
|
|
66
|
-
return useMutation<TData, TError, TParams>({
|
|
70
|
+
return useMutation<TData, TError, TParams, TMetadata>({
|
|
67
71
|
mutationFn,
|
|
68
72
|
...rest,
|
|
69
73
|
});
|
|
70
74
|
};
|
|
71
75
|
|
|
72
|
-
export function buildMutationHook<
|
|
73
|
-
|
|
74
|
-
|
|
76
|
+
export function buildMutationHook<
|
|
77
|
+
TFnToParams extends FnToParamsBase,
|
|
78
|
+
TMetadata = unknown,
|
|
79
|
+
>(
|
|
80
|
+
promiseFunc: TPromiseFunction<unknown, unknown, unknown>,
|
|
81
|
+
genTargetService: (meta?: TMetadata) => string
|
|
75
82
|
) {
|
|
76
83
|
return function useCallMutationBuilder<TFn extends keyof TFnToParams>(
|
|
77
84
|
func: TFn,
|
|
@@ -80,18 +87,18 @@ export function buildMutationHook<TFnToParams extends FnToParamsBase>(
|
|
|
80
87
|
TFnToParams,
|
|
81
88
|
TFnToParams[TFn]["data"],
|
|
82
89
|
TFnToParams[TFn]["error"],
|
|
83
|
-
TFnToParams[TFn]["params"]
|
|
90
|
+
TFnToParams[TFn]["params"],
|
|
91
|
+
TMetadata
|
|
84
92
|
>
|
|
85
|
-
>[3]
|
|
93
|
+
>[3]
|
|
86
94
|
) {
|
|
87
|
-
const targetService = genTargetService(options?.machine);
|
|
88
|
-
|
|
89
95
|
return useCallMutationBase<
|
|
90
96
|
TFnToParams,
|
|
91
97
|
TFnToParams[TFn]["data"],
|
|
92
98
|
TFnToParams[TFn]["error"],
|
|
93
|
-
TFnToParams[TFn]["params"]
|
|
94
|
-
|
|
99
|
+
TFnToParams[TFn]["params"],
|
|
100
|
+
TMetadata
|
|
101
|
+
>(promiseFunc, func, genTargetService, options);
|
|
95
102
|
};
|
|
96
103
|
}
|
|
97
104
|
|
|
@@ -101,32 +108,30 @@ const useCallQueryBase = <
|
|
|
101
108
|
TError = unknown,
|
|
102
109
|
TParams = unknown,
|
|
103
110
|
TSelect = TData,
|
|
111
|
+
TMetadata = unknown,
|
|
104
112
|
>(
|
|
105
|
-
promiseFunction: TPromiseFunction<TData, TParams>,
|
|
113
|
+
promiseFunction: TPromiseFunction<TData, TParams, TMetadata>,
|
|
106
114
|
functionName: keyof TFnToParams,
|
|
107
115
|
params: TParams,
|
|
108
|
-
service: string,
|
|
116
|
+
service: (meta?: TMetadata) => string,
|
|
109
117
|
options?: Omit<QueryOptions<TData, TError, TParams, TSelect>, "queryFn"> & {
|
|
110
|
-
|
|
111
|
-
customTimeout?: number;
|
|
118
|
+
meta?: TMetadata;
|
|
112
119
|
}
|
|
113
120
|
) => {
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
customTimeout = undefined,
|
|
117
|
-
...rest
|
|
118
|
-
} = useMemo(() => options ?? {}, [options]);
|
|
121
|
+
const timeout = options?.timeout;
|
|
122
|
+
const { ...rest } = useMemo(() => options ?? {}, [options]);
|
|
119
123
|
const queryFn = useCallback(
|
|
120
|
-
async (refetchParams: TParams | void
|
|
124
|
+
async (refetchParams: TParams | void) => {
|
|
121
125
|
try {
|
|
122
126
|
return await promiseFunction({
|
|
123
|
-
service:
|
|
127
|
+
service: service(options?.meta),
|
|
124
128
|
functionName: functionName.toString(),
|
|
125
129
|
parameters: refetchParams ?? params,
|
|
126
130
|
options: {
|
|
127
|
-
avoidTimeout,
|
|
128
|
-
customTimeout,
|
|
131
|
+
avoidTimeout: !timeout,
|
|
132
|
+
customTimeout: timeout,
|
|
129
133
|
},
|
|
134
|
+
meta: options?.meta,
|
|
130
135
|
});
|
|
131
136
|
} catch (e) {
|
|
132
137
|
if (e instanceof Error) {
|
|
@@ -135,7 +140,7 @@ const useCallQueryBase = <
|
|
|
135
140
|
throw new Error(e as string);
|
|
136
141
|
}
|
|
137
142
|
},
|
|
138
|
-
[
|
|
143
|
+
[functionName, service, params, timeout]
|
|
139
144
|
);
|
|
140
145
|
|
|
141
146
|
return useQuery<TData, TError, TParams, TSelect>({
|
|
@@ -144,9 +149,12 @@ const useCallQueryBase = <
|
|
|
144
149
|
});
|
|
145
150
|
};
|
|
146
151
|
|
|
147
|
-
export function buildQueryHook<
|
|
148
|
-
|
|
149
|
-
|
|
152
|
+
export function buildQueryHook<
|
|
153
|
+
TFnToParams extends FnToParamsBase,
|
|
154
|
+
TMetadata = unknown,
|
|
155
|
+
>(
|
|
156
|
+
promiseFunction: TPromiseFunction<unknown, unknown, unknown>,
|
|
157
|
+
getTargetService: (meta?: TMetadata) => string
|
|
150
158
|
) {
|
|
151
159
|
return function useCallQueryBuilder<
|
|
152
160
|
TFn extends keyof TFnToParams,
|
|
@@ -160,18 +168,18 @@ export function buildQueryHook<TFnToParams extends FnToParamsBase>(
|
|
|
160
168
|
TFnToParams[TFn]["data"],
|
|
161
169
|
TFnToParams[TFn]["error"],
|
|
162
170
|
TFnToParams[TFn]["params"],
|
|
163
|
-
TSelect
|
|
171
|
+
TSelect,
|
|
172
|
+
TMetadata
|
|
164
173
|
>
|
|
165
|
-
>[4] & {
|
|
174
|
+
>[4] & { meta?: TMetadata }
|
|
166
175
|
) {
|
|
167
|
-
const targetService = getTargetService(options?.machine);
|
|
168
|
-
|
|
169
176
|
return useCallQueryBase<
|
|
170
177
|
TFnToParams,
|
|
171
178
|
TFnToParams[TFn]["data"],
|
|
172
179
|
TFnToParams[TFn]["error"],
|
|
173
180
|
TFnToParams[TFn]["params"],
|
|
174
|
-
TSelect
|
|
175
|
-
|
|
181
|
+
TSelect,
|
|
182
|
+
TMetadata
|
|
183
|
+
>(promiseFunction, func, params, getTargetService, options);
|
|
176
184
|
};
|
|
177
185
|
}
|
package/src/proxyBinders.ts
CHANGED
|
@@ -17,7 +17,9 @@ export function bindClientSocketProxy(socketClientProxy: ZMQProxyWSClient) {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export function bindPromiseFunction(clientSocketProxy: ZMQProxyWSClient) {
|
|
20
|
-
return async (
|
|
20
|
+
return async (
|
|
21
|
+
...args: Parameters<TPromiseFunction<unknown, unknown, unknown>>
|
|
22
|
+
) => {
|
|
21
23
|
const res = await clientSocketProxy.callAsync(...args);
|
|
22
24
|
return res.message.Result;
|
|
23
25
|
};
|
|
@@ -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,40 +1,63 @@
|
|
|
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?: (
|
|
16
|
-
|
|
17
|
+
onSuccess?: (
|
|
18
|
+
data: TData,
|
|
19
|
+
variables: TParams,
|
|
20
|
+
meta?: TMetadata
|
|
21
|
+
) => Promise<unknown> | unknown;
|
|
22
|
+
onError?: (
|
|
23
|
+
error: TMutationError<TError>,
|
|
24
|
+
variables: TParams,
|
|
25
|
+
meta?: TMetadata
|
|
26
|
+
) => Promise<unknown> | unknown;
|
|
17
27
|
onSettled?: (
|
|
18
28
|
data: TData | undefined,
|
|
19
|
-
error: TError | null,
|
|
20
|
-
variables: TParams
|
|
29
|
+
error: TMutationError<TError> | null,
|
|
30
|
+
variables: TParams,
|
|
31
|
+
meta?: TMetadata
|
|
21
32
|
) => Promise<unknown> | unknown;
|
|
22
33
|
compareOrThrow?: (result: TData | TError) => boolean;
|
|
23
34
|
timeout?: number;
|
|
24
|
-
prepareParams?: (
|
|
35
|
+
prepareParams?: (
|
|
36
|
+
variables: TParams,
|
|
37
|
+
defaults?: TParams,
|
|
38
|
+
meta?: TMetadata
|
|
39
|
+
) => TParams;
|
|
25
40
|
}
|
|
26
41
|
|
|
42
|
+
type TMutationError<TError> = TError | Error | null;
|
|
43
|
+
|
|
27
44
|
export interface MutateOptions<
|
|
28
45
|
TData = unknown,
|
|
29
46
|
TError = unknown,
|
|
30
47
|
TParams = void,
|
|
48
|
+
TMetadata = unknown,
|
|
31
49
|
> {
|
|
32
|
-
onSuccess?: (data: TData, variables: TParams) => void;
|
|
33
|
-
onError?: (
|
|
50
|
+
onSuccess?: (data: TData, variables: TParams, meta?: TMetadata) => void;
|
|
51
|
+
onError?: (
|
|
52
|
+
error: TMutationError<TError>,
|
|
53
|
+
variables: TParams,
|
|
54
|
+
meta?: TMetadata
|
|
55
|
+
) => void;
|
|
34
56
|
onSettled?: (
|
|
35
57
|
data: TData | undefined,
|
|
36
|
-
error: TError | null,
|
|
37
|
-
variables: TParams
|
|
58
|
+
error: TMutationError<TError> | null,
|
|
59
|
+
variables: TParams,
|
|
60
|
+
meta?: TMetadata
|
|
38
61
|
) => void;
|
|
39
62
|
}
|
|
40
63
|
|
|
@@ -56,54 +79,13 @@ export class ComparisonFailError<TPayload = unknown> extends Error {
|
|
|
56
79
|
}
|
|
57
80
|
}
|
|
58
81
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
* @returns An object with the following properties:
|
|
67
|
-
* - `mutateAsync`: A function that takes in the variables and returns a promise that resolves to the
|
|
68
|
-
* result of the mutation. This function can be awaited to get the result of the mutation. Callbacks can
|
|
69
|
-
* be passed to this function to be called when the mutation is successful or when it fails. It's good idea
|
|
70
|
-
* to wrap this function in a try/catch block.
|
|
71
|
-
* - `mutate`: A function that takes in the variables and returns nothing. This function does not
|
|
72
|
-
* return a promise. Callbacks can be passed to this function to be called when the mutation is
|
|
73
|
-
* successful or when it fails.
|
|
74
|
-
* - `status`: The status of the mutation. Can be one of `idle`, `loading`, `success` or `error`.
|
|
75
|
-
* - `result`: The result of the mutation. This is undefined until the mutation is successful.
|
|
76
|
-
* - `reset`: A function that resets the mutation to the initial state.
|
|
77
|
-
* - `error`: The error object if the mutation has failed.
|
|
78
|
-
* @example
|
|
79
|
-
* ```typescript
|
|
80
|
-
* const mutationRes = useMutation({
|
|
81
|
-
* mutationFn: (variables) => {
|
|
82
|
-
* return ZMQHelper.callSomethingAsync(variables)
|
|
83
|
-
* },
|
|
84
|
-
* onSuccess: (data) => {
|
|
85
|
-
* // do something with the result
|
|
86
|
-
* },
|
|
87
|
-
* onError: (error) => {
|
|
88
|
-
* // do something with the error
|
|
89
|
-
* },
|
|
90
|
-
* onSettled: (data, error) => {
|
|
91
|
-
* // do something when the mutation is done
|
|
92
|
-
* }});
|
|
93
|
-
*
|
|
94
|
-
* // call the mutation
|
|
95
|
-
* const res = await mutationRes.mutateAsync({ foo: "bar" });
|
|
96
|
-
* // OR
|
|
97
|
-
* mutationRes.mutate({ foo: "bar" }, {
|
|
98
|
-
* onSuccess: (data) => {
|
|
99
|
-
* // do something with the result
|
|
100
|
-
* // for example show a toast message
|
|
101
|
-
* // stop local loading state
|
|
102
|
-
* });
|
|
103
|
-
* ```
|
|
104
|
-
*/
|
|
105
|
-
export const useMutation = <TData = unknown, TError = unknown, TParams = void>(
|
|
106
|
-
options: MutationOptions<TData, TError, TParams>
|
|
82
|
+
export const useMutation = <
|
|
83
|
+
TData = unknown,
|
|
84
|
+
TError = TMutationError<unknown>,
|
|
85
|
+
TParams = void,
|
|
86
|
+
TMetadata = unknown,
|
|
87
|
+
>(
|
|
88
|
+
options: MutationOptions<TData, TError, TParams, TMetadata>
|
|
107
89
|
) => {
|
|
108
90
|
const {
|
|
109
91
|
mutationFn,
|
|
@@ -116,7 +98,7 @@ export const useMutation = <TData = unknown, TError = unknown, TParams = void>(
|
|
|
116
98
|
timeout,
|
|
117
99
|
} = options;
|
|
118
100
|
const [status, setStatus] = useState<MutationStatus>("idle");
|
|
119
|
-
const [error, setError] = useState<TError
|
|
101
|
+
const [error, setError] = useState<TMutationError<TError>>(null);
|
|
120
102
|
const [result, setResult] = useState<TData | undefined>(undefined);
|
|
121
103
|
const mutationFnRef = useRef(mutationFn);
|
|
122
104
|
const internalOptionsRef = useRef({
|
|
@@ -133,84 +115,77 @@ export const useMutation = <TData = unknown, TError = unknown, TParams = void>(
|
|
|
133
115
|
const internalCallbackRef = useRef(internalCallbackRefistry);
|
|
134
116
|
internalCallbackRef.current = internalCallbackRefistry;
|
|
135
117
|
|
|
136
|
-
const mutateAsync = useCallback(
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
internalCallbackRef.current.prepareParams
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
|
148
140
|
)
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
),
|
|
152
|
-
|
|
153
|
-
? [
|
|
154
|
-
new Promise<RaceErrorReason>((resolve) =>
|
|
155
|
-
setTimeout(
|
|
156
|
-
() => resolve("timeout"),
|
|
157
|
-
internalOptionsRef.current.timeout
|
|
158
|
-
)
|
|
159
|
-
),
|
|
160
|
-
]
|
|
161
|
-
: []),
|
|
162
|
-
];
|
|
163
|
-
|
|
164
|
-
const res = await Promise.race(promises);
|
|
165
|
-
|
|
166
|
-
if (res === "timeout") {
|
|
167
|
-
throw new Error("timeout");
|
|
168
|
-
}
|
|
141
|
+
),
|
|
142
|
+
]
|
|
143
|
+
: []),
|
|
144
|
+
];
|
|
169
145
|
|
|
170
|
-
|
|
171
|
-
throw new ComparisonFailError(JSON.stringify(res), res);
|
|
172
|
-
}
|
|
146
|
+
const res = await Promise.race(promises);
|
|
173
147
|
|
|
174
|
-
|
|
175
|
-
|
|
148
|
+
if (res === "timeout") {
|
|
149
|
+
throw new Error("timeout");
|
|
150
|
+
}
|
|
176
151
|
|
|
177
|
-
|
|
178
|
-
|
|
152
|
+
if (internalCallbackRef.current.compareOrThrow?.(res)) {
|
|
153
|
+
throw new ComparisonFailError(JSON.stringify(res), res);
|
|
154
|
+
}
|
|
179
155
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
setError(error);
|
|
183
|
-
setStatus("error");
|
|
156
|
+
setResult(res);
|
|
157
|
+
setStatus("success");
|
|
184
158
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
159
|
+
internalCallbackRef.current.onSuccess?.(res, params, meta);
|
|
160
|
+
internalCallbackRef.current.onSettled?.(res, null, params, meta);
|
|
188
161
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
162
|
+
return res;
|
|
163
|
+
} catch (error: any) {
|
|
164
|
+
setError(error);
|
|
165
|
+
setStatus("error");
|
|
192
166
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
167
|
+
internalCallbackRef.current.onError?.(error, params, meta);
|
|
168
|
+
internalCallbackRef.current.onSettled?.(undefined, error, params, meta);
|
|
169
|
+
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
}, []);
|
|
198
173
|
|
|
199
174
|
const mutate = useCallback(
|
|
200
175
|
(
|
|
201
176
|
variables: TParams,
|
|
202
|
-
options?: MutateOptions<TData, TError, TParams> & {
|
|
203
|
-
|
|
177
|
+
options?: MutateOptions<TData, TError, TParams, TMetadata> & {
|
|
178
|
+
meta?: TMetadata;
|
|
204
179
|
}
|
|
205
180
|
) => {
|
|
206
|
-
mutateAsync(variables, options?.
|
|
181
|
+
mutateAsync(variables, options?.meta).then(
|
|
207
182
|
(data) => {
|
|
208
|
-
options?.onSuccess?.(data, variables);
|
|
209
|
-
options?.onSettled?.(data, null, variables);
|
|
183
|
+
options?.onSuccess?.(data, variables, options?.meta);
|
|
184
|
+
options?.onSettled?.(data, null, variables, options?.meta);
|
|
210
185
|
},
|
|
211
186
|
(error) => {
|
|
212
|
-
options?.onError?.(error, variables);
|
|
213
|
-
options?.onSettled?.(undefined, error, variables);
|
|
187
|
+
options?.onError?.(error, variables, options?.meta);
|
|
188
|
+
options?.onSettled?.(undefined, error, variables, options?.meta);
|
|
214
189
|
}
|
|
215
190
|
);
|
|
216
191
|
},
|
|
@@ -222,7 +197,11 @@ export const useMutation = <TData = unknown, TError = unknown, TParams = void>(
|
|
|
222
197
|
status,
|
|
223
198
|
result,
|
|
224
199
|
mutate,
|
|
225
|
-
reset: () =>
|
|
200
|
+
reset: () => {
|
|
201
|
+
setStatus("idle");
|
|
202
|
+
setError(null);
|
|
203
|
+
setResult(undefined);
|
|
204
|
+
},
|
|
226
205
|
error,
|
|
227
206
|
};
|
|
228
207
|
};
|