From 680346d41919ca662522be36a7cf05afd579e87b Mon Sep 17 00:00:00 2001 From: maxime Date: Thu, 30 Nov 2023 21:48:06 +0100 Subject: [PATCH] feat: added support for simple observable on mutations --- .../client/mutations/createMutationRunner.ts | 9 +- src/lib/queries/client/mutations/types.ts | 4 + .../react/mutations/useMutationState.test.tsx | 125 +++++++----------- 3 files changed, 61 insertions(+), 77 deletions(-) diff --git a/src/lib/queries/client/mutations/createMutationRunner.ts b/src/lib/queries/client/mutations/createMutationRunner.ts index 51bed6c..fc882b2 100644 --- a/src/lib/queries/client/mutations/createMutationRunner.ts +++ b/src/lib/queries/client/mutations/createMutationRunner.ts @@ -86,7 +86,14 @@ export const createMutationRunner = ({ mutationsRunning$.next(mutationsRunning$.getValue() + 1) }), switchOperator(({ args, options }) => { - const queryRunner$ = defer(() => from(options.mutationFn(args))).pipe( + const mutationFn = options.mutationFn + + const mutationFnObservable = + typeof mutationFn === "function" + ? defer(() => from(mutationFn(args))) + : mutationFn + + const queryRunner$ = mutationFnObservable.pipe( retryOnError(options), take(1), map((data) => ({ data, isError: false })), diff --git a/src/lib/queries/client/mutations/types.ts b/src/lib/queries/client/mutations/types.ts index a2283fe..de8159a 100644 --- a/src/lib/queries/client/mutations/types.ts +++ b/src/lib/queries/client/mutations/types.ts @@ -26,6 +26,9 @@ export type MutationStatus = "idle" | "pending" | "success" | "error" export type MutationKey = unknown[] +/** + * @todo this should be used in a lot of place so we can probably make a helper for that + */ export interface MutationFilters { /** * Match mutation key exactly @@ -60,6 +63,7 @@ export interface MutationObservedResult extends MutationResult { } export type MutationFn = + | Observable | ((arg: MutationArg) => Promise) | ((arg: MutationArg) => Observable) diff --git a/src/lib/queries/react/mutations/useMutationState.test.tsx b/src/lib/queries/react/mutations/useMutationState.test.tsx index 3227e77..00f8e4d 100644 --- a/src/lib/queries/react/mutations/useMutationState.test.tsx +++ b/src/lib/queries/react/mutations/useMutationState.test.tsx @@ -9,6 +9,7 @@ import { setActTimeout, sleep } from "../../../../tests/utils" +import { delay, of } from "rxjs" describe("useIsMutating", () => { it("should return the number of fetching mutations", async () => { @@ -63,6 +64,54 @@ describe("useIsMutating", () => { }, {}) }) + it("should return the number of fetching observables mutations", async () => { + const isMutatings: number[] = [] + const queryClient = createQueryClient() + + const mutation2 = of("data").pipe(delay(50)) + + function IsMutating() { + // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression + const isMutating = useIsMutating() + isMutatings.push(isMutating) + return null + } + + function Mutations() { + const { mutate: mutate1 } = useMutation({ + mutationKey: ["mutation1"], + mutationFn: () => of("data").pipe(delay(150)) + }) + const { mutate: mutate2 } = useMutation({ + mutationKey: ["mutation2"], + mutationFn: mutation2 + }) + + React.useEffect(() => { + mutate1() + setActTimeout(() => { + mutate2() + }, 50) + }, [mutate1, mutate2]) + + return null + } + + function Page() { + return ( +
+ + +
+ ) + } + + renderWithClient(queryClient, ) + await waitFor(() => { + expect(isMutatings).toEqual([0, 1, 2, 1, 0]) + }, {}) + }) + it("should filter correctly by mutationKey", async () => { const isMutatings: number[] = [] const queryClient = createQueryClient() @@ -184,79 +233,3 @@ describe("useIsMutating", () => { await waitFor(() => rendered.getByText("mutating: 1")) }) }) - -// describe('useMutationState', () => { -// describe('types', () => { -// it('should default to QueryState', () => { -// doNotExecute(() => { -// const result = useMutationState({ -// filters: { status: 'pending' }, -// }) - -// expectTypeOf(result).toEqualTypeOf>() -// }) -// }) -// it('should infer with select', () => { -// doNotExecute(() => { -// const result = useMutationState({ -// filters: { status: 'pending' }, -// select: (mutation) => mutation.state.status, -// }) - -// expectTypeOf(result).toEqualTypeOf>() -// }) -// }) -// }) -// it('should return variables after calling mutate', async () => { -// const queryClient = createQueryClient() -// const variables: Array> = [] -// const mutationKey = ['mutation'] - -// function Variables() { -// variables.push( -// useMutationState({ -// filters: { mutationKey, status: 'pending' }, -// select: (mutation) => mutation.state.variables, -// }), -// ) - -// return null -// } - -// function Mutate() { -// const { mutate, data } = useMutation({ -// mutationKey, -// mutationFn: async (input: number) => { -// await sleep(150) -// return 'data' + input -// }, -// }) - -// return ( -//
-// data: {data ?? 'null'} -// -//
-// ) -// } - -// function Page() { -// return ( -//
-// -// -//
-// ) -// } - -// const rendered = renderWithClient(queryClient, ) - -// await waitFor(() => rendered.getByText('data: null')) - -// fireEvent.click(rendered.getByRole('button', { name: /mutate/i })) - -// await waitFor(() => rendered.getByText('data: data1')) - -// expect(variables).toEqual([[], [1], []]) -// }) -// })