Skip to content

Commit 1a1bb82

Browse files
Ponyetsshuding
andauthored
fix(mutate): fix types of mutate/trigger; make mutate/trigger always return the result of fetcher (#2708)
fix(mutate): fix types of mutate/trigger; make mutate/trigger always return the result of MutatorCallback Co-authored-by: Shu Ding <[email protected]>
1 parent 0505d07 commit 1a1bb82

File tree

11 files changed

+94
-58
lines changed

11 files changed

+94
-58
lines changed

_internal/src/types.ts

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -310,11 +310,15 @@ export type MutatorCallback<Data = any> = (
310310
currentData?: Data
311311
) => Promise<undefined | Data> | undefined | Data
312312

313-
export type MutatorOptions<Data = any> = {
313+
/**
314+
* @typeParam Data - The type of the data related to the key
315+
* @typeParam MutationData - The type of the data returned by the mutator
316+
*/
317+
export type MutatorOptions<Data = any, MutationData = Data> = {
314318
revalidate?: boolean
315319
populateCache?:
316320
| boolean
317-
| ((result: any, currentData: Data | undefined) => Data)
321+
| ((result: MutationData, currentData: Data | undefined) => Data)
318322
optimisticData?:
319323
| Data
320324
| ((currentData: Data | undefined, displayedData: Data | undefined) => Data)
@@ -365,23 +369,38 @@ export type MutatorWrapper<Fn> = Fn extends (
365369

366370
export type Mutator<Data = any> = MutatorWrapper<MutatorFn<Data>>
367371

368-
export interface ScopedMutator<Data = any> {
369-
<T = Data>(
372+
export interface ScopedMutator {
373+
/**
374+
* @typeParam Data - The type of the data related to the key
375+
* @typeParam MutationData - The type of the data returned by the mutator
376+
*/
377+
<Data = any, MutationData = Data>(
370378
matcher: (key?: Arguments) => boolean,
371-
data?: T | Promise<T> | MutatorCallback<T>,
372-
opts?: boolean | MutatorOptions<Data>
373-
): Promise<Array<T | undefined>>
374-
<T = Data>(
379+
data?: MutationData | Promise<MutationData> | MutatorCallback<MutationData>,
380+
opts?: boolean | MutatorOptions<Data, MutationData>
381+
): Promise<Array<MutationData | undefined>>
382+
/**
383+
* @typeParam Data - The type of the data related to the key
384+
* @typeParam MutationData - The type of the data returned by the mutator
385+
*/
386+
<Data = any, T = Data>(
375387
key: Arguments,
376388
data?: T | Promise<T> | MutatorCallback<T>,
377-
opts?: boolean | MutatorOptions<Data>
389+
opts?: boolean | MutatorOptions<Data, T>
378390
): Promise<T | undefined>
379391
}
380392

381-
export type KeyedMutator<Data> = (
382-
data?: Data | Promise<Data | undefined> | MutatorCallback<Data>,
383-
opts?: boolean | MutatorOptions<Data>
384-
) => Promise<Data | undefined>
393+
/**
394+
* @typeParam Data - The type of the data related to the key
395+
* @typeParam MutationData - The type of the data returned by the mutator
396+
*/
397+
export type KeyedMutator<Data> = <MutationData>(
398+
data?:
399+
| MutationData
400+
| Promise<MutationData | undefined>
401+
| MutatorCallback<MutationData>,
402+
opts?: boolean | MutatorOptions<Data, MutationData>
403+
) => Promise<MutationData | undefined>
385404

386405
export type SWRConfiguration<
387406
Data = any,

_internal/src/utils/cache.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ export const initCache = <Data = any>(
2727
provider: Cache<Data>,
2828
options?: Partial<ProviderConfiguration>
2929
):
30-
| [Cache<Data>, ScopedMutator<Data>, () => void, () => void]
31-
| [Cache<Data>, ScopedMutator<Data>]
30+
| [Cache<Data>, ScopedMutator, () => void, () => void]
31+
| [Cache<Data>, ScopedMutator]
3232
| undefined => {
3333
// The global state for a specific provider will be used to deduplicate
3434
// requests and store listeners. As well as a mutate function that is bound to
@@ -43,10 +43,7 @@ export const initCache = <Data = any>(
4343
// new mutate function.
4444
const EVENT_REVALIDATORS = {}
4545

46-
const mutate = internalMutate.bind(
47-
UNDEFINED,
48-
provider
49-
) as ScopedMutator<Data>
46+
const mutate = internalMutate.bind(UNDEFINED, provider) as ScopedMutator
5047
let unmount = noop
5148

5249
const subscriptions: Record<string, ((current: any, prev: any) => void)[]> =

_internal/src/utils/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ const compare = (currentData: any, newData: any) =>
4141
stableHash(currentData) == stableHash(newData)
4242

4343
// Default cache provider
44-
const [cache, mutate] = initCache(new Map()) as [Cache<any>, ScopedMutator<any>]
44+
const [cache, mutate] = initCache(new Map()) as [Cache<any>, ScopedMutator]
4545
export { cache, mutate, compare }
4646

4747
// Default config

_internal/src/utils/mutate.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -174,10 +174,9 @@ export async function internalMutate<Data>(
174174
// Rollback. Always populate the cache in this case but without
175175
// transforming the data.
176176
populateCache = true
177-
data = committedData
178177

179178
// Reset data to be the latest committed data, and clear the `_c` value.
180-
set({ data, _c: UNDEFINED })
179+
set({ data: committedData, _c: UNDEFINED })
181180
}
182181
}
183182

@@ -186,29 +185,30 @@ export async function internalMutate<Data>(
186185
if (!error) {
187186
// Transform the result into data.
188187
if (isFunction(populateCache)) {
189-
data = populateCache(data, committedData)
188+
const populateCachedData = populateCache(data, committedData)
189+
set({ data: populateCachedData, error: UNDEFINED, _c: UNDEFINED })
190+
} else {
191+
// Only update cached data and reset the error if there's no error. Data can be `undefined` here.
192+
set({ data, error: UNDEFINED, _c: UNDEFINED })
190193
}
191-
192-
// Only update cached data and reset the error if there's no error. Data can be `undefined` here.
193-
set({ data, error: UNDEFINED, _c: UNDEFINED })
194194
}
195195
}
196196

197197
// Reset the timestamp to mark the mutation has ended.
198198
MUTATION[key][1] = getTimestamp()
199199

200200
// Update existing SWR Hooks' internal states:
201-
const res = await startRevalidate()
202-
203-
// The mutation and revalidation are ended, we can clear it since the data is
204-
// not an optimistic value anymore.
205-
set({ _c: UNDEFINED })
201+
Promise.resolve(startRevalidate()).then(() => {
202+
// The mutation and revalidation are ended, we can clear it since the data is
203+
// not an optimistic value anymore.
204+
set({ _c: UNDEFINED })
205+
})
206206

207207
// Throw error or return data
208208
if (error) {
209209
if (throwOnError) throw error
210210
return
211211
}
212-
return populateCache ? res : data
212+
return data
213213
}
214214
}

core/src/use-swr.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,7 @@ export const useSWRHandler = <Data = any, Error = any>(
550550
// eslint-disable-next-line react-hooks/exhaustive-deps
551551
const boundMutate: SWRResponse<Data, Error>['mutate'] = useCallback(
552552
// Use callback to make sure `keyRef.current` returns latest result every time
553-
(...args) => {
553+
(...args: any[]) => {
554554
return internalMutate(cache, keyRef.current, ...args)
555555
},
556556
// eslint-disable-next-line react-hooks/exhaustive-deps

infinite/src/index.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -232,13 +232,9 @@ export const infinite = (<Data, Error>(useSWRNext: SWRHook) =>
232232

233233
const mutate = useCallback(
234234
// eslint-disable-next-line func-names
235-
function (
236-
data?:
237-
| undefined
238-
| Data[]
239-
| Promise<Data[] | undefined>
240-
| MutatorCallback<Data[]>,
241-
opts?: undefined | boolean | MutatorOptions<Data[]>
235+
function <T>(
236+
data?: undefined | T | Promise<T | undefined> | MutatorCallback<T>,
237+
opts?: undefined | boolean | MutatorOptions<Data[], T>
242238
) {
243239
// When passing as a boolean, it's explicitly used to disable/enable
244240
// revalidation.
@@ -261,8 +257,8 @@ export const infinite = (<Data, Error>(useSWRNext: SWRHook) =>
261257
}
262258

263259
return arguments.length
264-
? swr.mutate(data, { ...options, revalidate: shouldRevalidate })
265-
: swr.mutate()
260+
? swr.mutate<T>(data, { ...options, revalidate: shouldRevalidate })
261+
: swr.mutate<T>()
266262
},
267263
// swr.mutate is always the same reference
268264
// eslint-disable-next-line react-hooks/exhaustive-deps

mutation/src/types.ts

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,13 @@ export interface SWRMutationResponse<
190190
}
191191

192192
export interface SWRMutationHook {
193-
<Data = any, Error = any, SWRMutationKey extends Key = Key, ExtraArg = never>(
193+
<
194+
Data = any,
195+
Error = any,
196+
SWRMutationKey extends Key = Key,
197+
ExtraArg = never,
198+
SWRData = Data
199+
>(
194200
/**
195201
* The key of the resource that will be mutated. It should be the same key
196202
* used in the `useSWR` hook so SWR can handle revalidation and race
@@ -212,9 +218,21 @@ export interface SWRMutationHook {
212218
/**
213219
* Extra options for the mutation hook.
214220
*/
215-
options?: SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg>
221+
options?: SWRMutationConfiguration<
222+
Data,
223+
Error,
224+
SWRMutationKey,
225+
ExtraArg,
226+
SWRData
227+
>
216228
): SWRMutationResponse<Data, Error, SWRMutationKey, ExtraArg>
217-
<Data = any, Error = any, SWRMutationKey extends Key = Key, ExtraArg = never>(
229+
<
230+
Data = any,
231+
Error = any,
232+
SWRMutationKey extends Key = Key,
233+
ExtraArg = never,
234+
SWRData = Data
235+
>(
218236
/**
219237
* The key of the resource that will be mutated. It should be the same key
220238
* used in the `useSWR` hook so SWR can handle revalidation and race
@@ -240,10 +258,17 @@ export interface SWRMutationHook {
240258
Data,
241259
Error,
242260
SWRMutationKey,
243-
ExtraArg
261+
ExtraArg,
262+
SWRData
244263
> & { throwOnError: false }
245264
): SWRMutationResponse<Data | undefined, Error, SWRMutationKey, ExtraArg>
246-
<Data = any, Error = any, SWRMutationKey extends Key = Key, ExtraArg = never>(
265+
<
266+
Data = any,
267+
Error = any,
268+
SWRMutationKey extends Key = Key,
269+
ExtraArg = never,
270+
SWRData = Data
271+
>(
247272
/**
248273
* The key of the resource that will be mutated. It should be the same key
249274
* used in the `useSWR` hook so SWR can handle revalidation and race
@@ -269,7 +294,8 @@ export interface SWRMutationHook {
269294
Data,
270295
Error,
271296
SWRMutationKey,
272-
ExtraArg
297+
ExtraArg,
298+
SWRData
273299
> & { throwOnError: true }
274300
): SWRMutationResponse<Data, Error, SWRMutationKey, ExtraArg>
275301
}

subscription/src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { Key, SWRConfiguration, MutatorCallback } from 'swr'
22

33
export type SWRSubscriptionOptions<Data = any, Error = any> = {
4-
next: <T = Data>(err?: Error | null, data?: Data | MutatorCallback<T>) => void
4+
next: (err?: Error | null, data?: Data | MutatorCallback<Data>) => void
55
}
66

77
export type SWRSubscription<

test/type/mutate.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,9 @@ export function useMutatorTypes() {
6262

6363
mutate(async () => '1')
6464

65-
// @ts-expect-error
6665
mutate(async () => 1)
6766

68-
// FIXME: this should work.
69-
// mutate(async () => 1, { populateCache: false })
67+
mutate(async () => 1, { populateCache: false })
7068
}
7169

7270
export function useConfigMutate() {
@@ -85,7 +83,7 @@ export function useConfigMutate() {
8583
)
8684

8785
expect<Promise<any>>(
88-
mutate('string', data => {
86+
mutate('string', (data?: string) => {
8987
expectType<string | undefined>(data)
9088
return '0'
9189
})

test/use-swr-infinite.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,7 +1394,7 @@ describe('useSWRInfinite', () => {
13941394
<button
13951395
onClick={() => {
13961396
mutate(updater, {
1397-
populateCache: (result: string, currentData: string[]) => {
1397+
populateCache: (result: string[], currentData: string[]) => {
13981398
return [...currentData, ...result]
13991399
},
14001400
revalidate: false
@@ -1476,7 +1476,7 @@ describe('useSWRInfinite', () => {
14761476
onClick={() => {
14771477
mutate(updater, {
14781478
optimisticData: current => [current[0], [...current[1], 'B4']],
1479-
populateCache: (result: string, currentData: string[]) => {
1479+
populateCache: (result: string[], currentData: string[]) => {
14801480
return [currentData[0], [...currentData[1], ...result]]
14811481
},
14821482
revalidate: false

0 commit comments

Comments
 (0)