Skip to content

Commit

Permalink
fix(mutate): fix types of mutate/trigger; make mutate/trigger always …
Browse files Browse the repository at this point in the history
…return the result of MutatorCallback
  • Loading branch information
Ponyets committed Jul 14, 2023
1 parent 5e5e01a commit abdcdae
Show file tree
Hide file tree
Showing 9 changed files with 76 additions and 53 deletions.
29 changes: 16 additions & 13 deletions _internal/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,14 +312,17 @@ export type MutatorCallback<Data = any> = (
currentData?: Data
) => Promise<undefined | Data> | undefined | Data

export type MutatorOptions<Data = any> = {
export type MutatorOptions<SWRData = any, Data = SWRData> = {
revalidate?: boolean
populateCache?:
| boolean
| ((result: any, currentData: Data | undefined) => Data)
| ((result: Data, currentData: SWRData | undefined) => SWRData)
optimisticData?:
| Data
| ((currentData: Data | undefined, displayedData: Data | undefined) => Data)
| SWRData
| ((
currentData: SWRData | undefined,
displayedData: SWRData | undefined
) => SWRData)
rollbackOnError?: boolean | ((error: unknown) => boolean)
throwOnError?: boolean
}
Expand Down Expand Up @@ -367,23 +370,23 @@ export type MutatorWrapper<Fn> = Fn extends (

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

export interface ScopedMutator<Data = any> {
<T = Data>(
export interface ScopedMutator {
<Data = any, T = Data>(
matcher: (key?: Arguments) => boolean,
data?: T | Promise<T> | MutatorCallback<T>,
opts?: boolean | MutatorOptions<Data>
opts?: boolean | MutatorOptions<Data, T>
): Promise<Array<T | undefined>>
<T = Data>(
<Data = any, T = Data>(
key: Arguments,
data?: T | Promise<T> | MutatorCallback<T>,
opts?: boolean | MutatorOptions<Data>
opts?: boolean | MutatorOptions<Data, T>
): Promise<T | undefined>
}

export type KeyedMutator<Data> = (
data?: Data | Promise<Data | undefined> | MutatorCallback<Data>,
opts?: boolean | MutatorOptions<Data>
) => Promise<Data | undefined>
export type KeyedMutator<SWRData> = <T>(
data?: T | Promise<T | undefined> | MutatorCallback<T>,
opts?: boolean | MutatorOptions<SWRData, T>
) => Promise<T | undefined>

export type SWRConfiguration<
Data = any,
Expand Down
9 changes: 3 additions & 6 deletions _internal/src/utils/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ export const initCache = <Data = any>(
provider: Cache<Data>,
options?: Partial<ProviderConfiguration>
):
| [Cache<Data>, ScopedMutator<Data>, () => void, () => void]
| [Cache<Data>, ScopedMutator<Data>]
| [Cache<Data>, ScopedMutator, () => void, () => void]
| [Cache<Data>, ScopedMutator]
| undefined => {
// The global state for a specific provider will be used to deduplicate
// requests and store listeners. As well as a mutate function that is bound to
Expand All @@ -43,10 +43,7 @@ export const initCache = <Data = any>(
// new mutate function.
const EVENT_REVALIDATORS = {}

const mutate = internalMutate.bind(
UNDEFINED,
provider
) as ScopedMutator<Data>
const mutate = internalMutate.bind(UNDEFINED, provider) as ScopedMutator
let unmount = noop

const subscriptions: Record<string, ((current: any, prev: any) => void)[]> =
Expand Down
2 changes: 1 addition & 1 deletion _internal/src/utils/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const compare = (currentData: any, newData: any) =>
stableHash(currentData) == stableHash(newData)

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

// Default config
Expand Down
24 changes: 12 additions & 12 deletions _internal/src/utils/mutate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,9 @@ export async function internalMutate<Data>(
// Rollback. Always populate the cache in this case but without
// transforming the data.
populateCache = true
data = committedData

// Reset data to be the latest committed data, and clear the `_c` value.
set({ data, _c: UNDEFINED })
set({ data: committedData, _c: UNDEFINED })
}
}

Expand All @@ -186,29 +185,30 @@ export async function internalMutate<Data>(
if (!error) {
// Transform the result into data.
if (isFunction(populateCache)) {
data = populateCache(data, committedData)
const populateCachedData = populateCache(data, committedData)
set({ data: populateCachedData, error: UNDEFINED, _c: UNDEFINED })
} else {
// Only update cached data and reset the error if there's no error. Data can be `undefined` here.
set({ data, error: UNDEFINED, _c: UNDEFINED })
}

// Only update cached data and reset the error if there's no error. Data can be `undefined` here.
set({ data, error: UNDEFINED, _c: UNDEFINED })
}
}

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

// Update existing SWR Hooks' internal states:
const res = await startRevalidate()

// The mutation and revalidation are ended, we can clear it since the data is
// not an optimistic value anymore.
set({ _c: UNDEFINED })
Promise.resolve(startRevalidate()).then(() => {
// The mutation and revalidation are ended, we can clear it since the data is
// not an optimistic value anymore.
set({ _c: UNDEFINED })
})

// Throw error or return data
if (error) {
if (throwOnError) throw error
return
}
return populateCache ? res : data
return data
}
}
2 changes: 1 addition & 1 deletion core/src/use-swr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,7 @@ export const useSWRHandler = <Data = any, Error = any>(
// eslint-disable-next-line react-hooks/exhaustive-deps
const boundMutate: SWRResponse<Data, Error>['mutate'] = useCallback(
// Use callback to make sure `keyRef.current` returns latest result every time
(...args) => {
(...args: any[]) => {
return internalMutate(cache, keyRef.current, ...args)
},
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down
17 changes: 7 additions & 10 deletions infinite/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,13 +218,9 @@ export const infinite = (<Data, Error>(useSWRNext: SWRHook) =>

const mutate = useCallback(
// eslint-disable-next-line func-names
function (
data?:
| undefined
| Data[]
| Promise<Data[] | undefined>
| MutatorCallback<Data[]>,
opts?: undefined | boolean | MutatorOptions<Data[]>
function <T>(
data?: undefined | T | Promise<T | undefined> | MutatorCallback<T>,
opts?: undefined | boolean | MutatorOptions<Data[], T>
) {
// When passing as a boolean, it's explicitly used to disable/enable
// revalidation.
Expand All @@ -246,9 +242,10 @@ export const infinite = (<Data, Error>(useSWRNext: SWRHook) =>
}
}

return arguments.length
? swr.mutate(data, { ...options, revalidate: shouldRevalidate })
: swr.mutate()
const result = arguments.length
? swr.mutate<T>(data, { ...options, revalidate: shouldRevalidate })
: swr.mutate<T>()
return result
},
// swr.mutate is always the same reference
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down
38 changes: 32 additions & 6 deletions mutation/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,13 @@ export interface SWRMutationResponse<
}

export interface SWRMutationHook {
<Data = any, Error = any, SWRMutationKey extends Key = Key, ExtraArg = never>(
<
Data = any,
Error = any,
SWRMutationKey extends Key = Key,
ExtraArg = never,
SWRData = Data
>(
/**
* The key of the resource that will be mutated. It should be the same key
* used in the `useSWR` hook so SWR can handle revalidation and race
Expand All @@ -212,9 +218,21 @@ export interface SWRMutationHook {
/**
* Extra options for the mutation hook.
*/
options?: SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg>
options?: SWRMutationConfiguration<
Data,
Error,
SWRMutationKey,
ExtraArg,
SWRData
>
): SWRMutationResponse<Data, Error, SWRMutationKey, ExtraArg>
<Data = any, Error = any, SWRMutationKey extends Key = Key, ExtraArg = never>(
<
Data = any,
Error = any,
SWRMutationKey extends Key = Key,
ExtraArg = never,
SWRData = Data
>(
/**
* The key of the resource that will be mutated. It should be the same key
* used in the `useSWR` hook so SWR can handle revalidation and race
Expand All @@ -240,10 +258,17 @@ export interface SWRMutationHook {
Data,
Error,
SWRMutationKey,
ExtraArg
ExtraArg,
SWRData
> & { throwOnError: false }
): SWRMutationResponse<Data | undefined, Error, SWRMutationKey, ExtraArg>
<Data = any, Error = any, SWRMutationKey extends Key = Key, ExtraArg = never>(
<
Data = any,
Error = any,
SWRMutationKey extends Key = Key,
ExtraArg = never,
SWRData = Data
>(
/**
* The key of the resource that will be mutated. It should be the same key
* used in the `useSWR` hook so SWR can handle revalidation and race
Expand All @@ -269,7 +294,8 @@ export interface SWRMutationHook {
Data,
Error,
SWRMutationKey,
ExtraArg
ExtraArg,
SWRData
> & { throwOnError: true }
): SWRMutationResponse<Data, Error, SWRMutationKey, ExtraArg>
}
2 changes: 1 addition & 1 deletion subscription/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Key, SWRConfiguration, MutatorCallback } from 'swr'

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

export type SWRSubscription<
Expand Down
6 changes: 3 additions & 3 deletions test/use-swr-local-mutation.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1365,7 +1365,7 @@ describe('useSWR - local mutation', () => {
}
)
})

await sleep(30)
try {
// data == "baz", then reverted back to "bar"
await executeWithoutBatching(() =>
Expand Down Expand Up @@ -1567,8 +1567,8 @@ describe('useSWR - local mutation', () => {

let appendData

const sendRequest = <Data,>(newItem) => {
return new Promise<Data>(res =>
const sendRequest = (newItem: string) => {
return new Promise<string>(res =>
setTimeout(() => {
// The server capitalizes the new item.
const modifiedData =
Expand Down

0 comments on commit abdcdae

Please sign in to comment.