-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
feat(vue-query): Add mutationOptions #10036
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
7e39464
f25452d
f064434
fb9d110
ded9c25
68a865b
f2ccd48
f05df98
6abcbfb
4f516be
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| '@tanstack/vue-query': minor | ||
| --- | ||
|
|
||
| Add mutationOptions. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| id: mutationOptions | ||
| title: mutationOptions | ||
| ref: docs/framework/react/reference/mutationOptions.md | ||
| --- |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,164 @@ | ||
| import { assertType, describe, expectTypeOf, it } from 'vitest' | ||
| import { reactive, ref } from 'vue-demi' | ||
| import { useIsMutating, useMutationState } from '../useMutationState' | ||
| import { useMutation } from '../useMutation' | ||
| import { mutationOptions } from '../mutationOptions' | ||
| import type { | ||
| DefaultError, | ||
| MutationFunctionContext, | ||
| MutationState, | ||
| } from '@tanstack/query-core' | ||
|
|
||
| describe('mutationOptions', () => { | ||
| it('should not allow excess properties', () => { | ||
| // @ts-expect-error this is a good error, because onMutates does not exist! | ||
| mutationOptions({ | ||
| mutationFn: () => Promise.resolve(5), | ||
| mutationKey: ['key'], | ||
| onMutates: 1000, | ||
| onSuccess: (data) => { | ||
| expectTypeOf(data).toEqualTypeOf<number>() | ||
| }, | ||
| }) | ||
| }) | ||
|
|
||
| it('should infer types for callbacks', () => { | ||
| mutationOptions({ | ||
| mutationFn: () => Promise.resolve(5), | ||
| mutationKey: ['key'], | ||
| onSuccess: (data) => { | ||
| expectTypeOf(data).toEqualTypeOf<number>() | ||
| }, | ||
| }) | ||
| }) | ||
|
|
||
| it('should infer types for onError callback', () => { | ||
| mutationOptions({ | ||
| mutationFn: () => { | ||
| throw new Error('fail') | ||
| }, | ||
| mutationKey: ['key'], | ||
| onError: (error) => { | ||
| expectTypeOf(error).toEqualTypeOf<DefaultError>() | ||
| }, | ||
| }) | ||
| }) | ||
|
|
||
| it('should infer types for variables', () => { | ||
| mutationOptions<number, DefaultError, { id: string }>({ | ||
| mutationFn: (vars) => { | ||
| expectTypeOf(vars).toEqualTypeOf<{ id: string }>() | ||
| return Promise.resolve(5) | ||
| }, | ||
| mutationKey: ['with-vars'], | ||
| }) | ||
| }) | ||
|
|
||
| it('should infer result type correctly', () => { | ||
| mutationOptions<number, DefaultError, void, { name: string }>({ | ||
| mutationFn: () => Promise.resolve(5), | ||
| mutationKey: ['key'], | ||
| onMutate: () => { | ||
| return { name: 'onMutateResult' } | ||
| }, | ||
| onSuccess: (_data, _variables, onMutateResult) => { | ||
| expectTypeOf(onMutateResult).toEqualTypeOf<{ name: string }>() | ||
| }, | ||
| }) | ||
| }) | ||
|
|
||
| it('should infer context type correctly', () => { | ||
| mutationOptions<number>({ | ||
| mutationFn: (_variables, context) => { | ||
| expectTypeOf(context).toEqualTypeOf<MutationFunctionContext>() | ||
| return Promise.resolve(5) | ||
| }, | ||
| mutationKey: ['key'], | ||
| onMutate: (_variables, context) => { | ||
| expectTypeOf(context).toEqualTypeOf<MutationFunctionContext>() | ||
| }, | ||
| onSuccess: (_data, _variables, _onMutateResult, context) => { | ||
| expectTypeOf(context).toEqualTypeOf<MutationFunctionContext>() | ||
| }, | ||
| onError: (_error, _variables, _onMutateResult, context) => { | ||
| expectTypeOf(context).toEqualTypeOf<MutationFunctionContext>() | ||
| }, | ||
| onSettled: (_data, _error, _variables, _onMutateResult, context) => { | ||
| expectTypeOf(context).toEqualTypeOf<MutationFunctionContext>() | ||
| }, | ||
| }) | ||
| }) | ||
|
|
||
| it('should error if mutationFn return type mismatches TData', () => { | ||
| assertType( | ||
| mutationOptions<number>({ | ||
| // @ts-expect-error this is a good error, because return type is string, not number | ||
| mutationFn: async () => Promise.resolve('wrong return'), | ||
| }), | ||
| ) | ||
| }) | ||
|
|
||
| it('should allow mutationKey to be omitted', () => { | ||
| return mutationOptions({ | ||
| mutationFn: () => Promise.resolve(123), | ||
| onSuccess: (data) => { | ||
| expectTypeOf(data).toEqualTypeOf<number>() | ||
| }, | ||
| }) | ||
| }) | ||
|
|
||
| it('should infer types when used with useMutation', () => { | ||
| const mutation = reactive( | ||
| useMutation( | ||
| mutationOptions({ | ||
| mutationKey: ['key'], | ||
| mutationFn: () => Promise.resolve('data'), | ||
| onSuccess: (data) => { | ||
| expectTypeOf(data).toEqualTypeOf<string>() | ||
| }, | ||
| }), | ||
| ), | ||
| ) | ||
| expectTypeOf(mutation.data).toEqualTypeOf<string | undefined>() | ||
|
|
||
| reactive( | ||
| useMutation( | ||
| // should allow when used with useMutation without mutationKey | ||
| mutationOptions({ | ||
| mutationFn: () => Promise.resolve('data'), | ||
| onSuccess: (data) => { | ||
| expectTypeOf(data).toEqualTypeOf<string>() | ||
| }, | ||
| }), | ||
| ), | ||
| ) | ||
| }) | ||
|
|
||
| it('should infer types when used with useIsMutating', () => { | ||
| const isMutating = useIsMutating({ | ||
| mutationKey: ['key'], | ||
| }) | ||
| expectTypeOf(isMutating.value).toEqualTypeOf<number>() | ||
| }) | ||
|
|
||
| it('should infer types when used with useMutationState', () => { | ||
| const mutationState = useMutationState({ | ||
| filters: { | ||
| mutationKey: ['key'], | ||
| }, | ||
| }) | ||
| expectTypeOf(mutationState.value).toEqualTypeOf< | ||
| Array<MutationState<unknown, Error, unknown, unknown>> | ||
| >() | ||
| }) | ||
|
|
||
| it('should allow to be passed to useMutation while containing ref in mutationKey', () => { | ||
| const options = mutationOptions({ | ||
| mutationKey: ['key', ref(1), { nested: ref(2) }], | ||
| mutationFn: () => Promise.resolve(5), | ||
| }) | ||
|
|
||
| const mutation = reactive(useMutation(options)) | ||
| expectTypeOf(mutation.data).toEqualTypeOf<number | undefined>() | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,22 @@ | ||||||
| import { describe, expect, it } from 'vitest' | ||||||
| import { sleep } from '@tanstack/query-test-utils' | ||||||
| import { mutationOptions } from '../mutationOptions' | ||||||
|
|
||||||
| describe('mutationOptions', () => { | ||||||
| it('should return the object received as a parameter without any modification (with mutationKey in mutationOptions)', () => { | ||||||
| const object = { | ||||||
| mutationKey: ['key'], | ||||||
| mutationFn: () => sleep(10).then(() => 5), | ||||||
| } as const | ||||||
|
|
||||||
| expect(mutationOptions(object)).toStrictEqual(object) | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe Perhaps we should use both |
||||||
| }) | ||||||
|
|
||||||
| it('should return the object received as a parameter without any modification (without mutationKey in mutationOptions)', () => { | ||||||
| const object = { | ||||||
| mutationFn: () => sleep(10).then(() => 5), | ||||||
| } as const | ||||||
|
|
||||||
| expect(mutationOptions(object)).toStrictEqual(object) | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| }) | ||||||
| }) | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| import type { UseMutationOptions } from './useMutation' | ||
| import type { DefaultError, WithRequired } from '@tanstack/query-core' | ||
| import type { DeepUnwrapRefOrGetter, MaybeRefDeepOrGetter } from './types' | ||
|
|
||
| export function mutationOptions< | ||
| TData = unknown, | ||
| TError = DefaultError, | ||
| TVariables = void, | ||
| TOnMutateResult = unknown, | ||
| >( | ||
| options: MaybeRefDeepOrGetter< | ||
| WithRequired< | ||
| DeepUnwrapRefOrGetter< | ||
| UseMutationOptions<TData, TError, TVariables, TOnMutateResult> | ||
| >, | ||
| 'mutationKey' | ||
| > | ||
| >, | ||
| ): MaybeRefDeepOrGetter< | ||
| WithRequired< | ||
| DeepUnwrapRefOrGetter< | ||
| UseMutationOptions<TData, TError, TVariables, TOnMutateResult> | ||
| >, | ||
| 'mutationKey' | ||
| > | ||
| > | ||
| export function mutationOptions< | ||
| TData = unknown, | ||
| TError = DefaultError, | ||
| TVariables = void, | ||
| TOnMutateResult = unknown, | ||
| >( | ||
| options: MaybeRefDeepOrGetter< | ||
| Omit< | ||
| DeepUnwrapRefOrGetter< | ||
| UseMutationOptions<TData, TError, TVariables, TOnMutateResult> | ||
| >, | ||
| 'mutationKey' | ||
| > | ||
| >, | ||
| ): MaybeRefDeepOrGetter< | ||
| Omit< | ||
| DeepUnwrapRefOrGetter< | ||
| UseMutationOptions<TData, TError, TVariables, TOnMutateResult> | ||
| >, | ||
| 'mutationKey' | ||
| > | ||
| > | ||
| export function mutationOptions< | ||
| TData = unknown, | ||
| TError = DefaultError, | ||
| TVariables = void, | ||
| TOnMutateResult = unknown, | ||
| >( | ||
| options: UseMutationOptions<TData, TError, TVariables, TOnMutateResult>, | ||
| ): UseMutationOptions<TData, TError, TVariables, TOnMutateResult> { | ||
| return options | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.