Skip to content

Commit

Permalink
Merge branch 'main' into shu/agil
Browse files Browse the repository at this point in the history
  • Loading branch information
shuding committed Feb 16, 2024
2 parents 7ca9d60 + 79445e4 commit 58af2ad
Show file tree
Hide file tree
Showing 12 changed files with 294 additions and 30 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "swr",
"version": "2.2.4",
"version": "2.2.5",
"description": "React Hooks library for remote data fetching",
"keywords": [
"swr",
Expand Down Expand Up @@ -106,7 +106,7 @@
"build": "bunchee",
"build:e2e": "pnpm next build e2e/site",
"attw": "attw --pack",
"types:check": "pnpm -r run types:check",
"types:check": "tsc --noEmit",
"prepublishOnly": "pnpm clean && pnpm build",
"publish-beta": "pnpm publish --tag beta",
"format": "prettier --write ./**/*.{ts,tsx}",
Expand Down
7 changes: 5 additions & 2 deletions src/_internal/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ export type MutatorCallback<Data = any> = (
* @typeParam MutationData - The type of the data returned by the mutator
*/
export type MutatorOptions<Data = any, MutationData = Data> = {
revalidate?: boolean
revalidate?: boolean | ((data: Data, key: Arguments) => boolean)
populateCache?:
| boolean
| ((result: MutationData, currentData: Data | undefined) => Data)
Expand Down Expand Up @@ -448,7 +448,10 @@ export type SWRConfiguration<
Data = any,
Error = any,
Fn extends BareFetcher<any> = BareFetcher<any>
> = Partial<PublicConfiguration<Data, Error, Fn>>
> = Partial<PublicConfiguration<Data, Error, Fn>> &
Partial<ProviderConfiguration> & {
provider?: (cache: Readonly<Cache>) => Cache
}

export type IsLoadingResponse<
Data = any,
Expand Down
16 changes: 4 additions & 12 deletions src/_internal/utils/config-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,15 @@ import { initCache } from './cache'
import { mergeConfigs } from './merge-config'
import { UNDEFINED, mergeObjects, isFunction } from './shared'
import { useIsomorphicLayoutEffect } from './env'
import type {
SWRConfiguration,
FullConfiguration,
ProviderConfiguration,
Cache
} from '../types'

type Config = SWRConfiguration &
Partial<ProviderConfiguration> & {
provider?: (cache: Readonly<Cache>) => Cache
}
import type { SWRConfiguration, FullConfiguration } from '../types'

export const SWRConfigContext = createContext<Partial<FullConfiguration>>({})

const SWRConfig: FC<
PropsWithChildren<{
value?: Config | ((parentConfig?: Config) => Config)
value?:
| SWRConfiguration
| ((parentConfig?: SWRConfiguration) => SWRConfiguration)
}>
> = props => {
const { value } = props
Expand Down
4 changes: 3 additions & 1 deletion src/_internal/utils/mutate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ export async function internalMutate<Data>(
const rollbackOnErrorOption = options.rollbackOnError
let optimisticData = options.optimisticData

const revalidate = options.revalidate !== false
const rollbackOnError = (error: unknown): boolean => {
return typeof rollbackOnErrorOption === 'function'
? rollbackOnErrorOption(error)
Expand Down Expand Up @@ -99,6 +98,9 @@ export async function internalMutate<Data>(

const startRevalidate = () => {
const revalidators = EVENT_REVALIDATORS[key]
const revalidate = isFunction(options.revalidate)
? options.revalidate(get().data, _k)
: options.revalidate !== false
if (revalidate) {
// Invalidate the key by deleting the concurrent request markers so new
// requests will not be deduped.
Expand Down
2 changes: 1 addition & 1 deletion src/core/use-swr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ export const useSWRHandler = <Data = any, Error = any>(
(isFunction(shouldRetryOnError) &&
shouldRetryOnError(err as Error))
) {
if (isActive()) {
if (!getConfig().revalidateOnFocus || !getConfig().revalidateOnReconnect || isActive()) {
// If it's inactive, stop. It will auto-revalidate when
// refocusing or reconnecting.
// When retrying, deduplication is always enabled.
Expand Down
21 changes: 14 additions & 7 deletions src/infinite/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import type {
SWRHook,
MutatorCallback,
Middleware,
MutatorOptions,
GlobalState
} from '../_internal'
import type {
Expand All @@ -31,7 +30,8 @@ import type {
SWRInfiniteKeyLoader,
SWRInfiniteFetcher,
SWRInfiniteCacheValue,
SWRInfiniteCompareFn
SWRInfiniteCompareFn,
SWRInfiniteMutatorOptions
} from './types'
import { useSyncExternalStore } from 'use-sync-external-store/shim/index.js'
import { getFirstPageKey } from './serialize'
Expand All @@ -55,7 +55,7 @@ export const infinite = (<Data, Error>(useSWRNext: SWRHook) =>
fn: BareFetcher<Data> | null,
config: Omit<typeof SWRConfig.defaultValue, 'fetcher'> &
Omit<SWRInfiniteConfiguration<Data, Error>, 'fetcher'>
): SWRInfiniteResponse<Data, Error> => {
) => {
const didMountRef = useRef<boolean>(false)
const {
cache,
Expand Down Expand Up @@ -140,6 +140,8 @@ export const infinite = (<Data, Error>(useSWRNext: SWRHook) =>
async key => {
// get the revalidate context
const forceRevalidateAll = get()._i
const shouldRevalidatePage = get()._r
set({ _r: UNDEFINED })

// return an array of page data
const data: Data[] = []
Expand Down Expand Up @@ -187,7 +189,12 @@ export const infinite = (<Data, Error>(useSWRNext: SWRHook) =>
(cacheData &&
!isUndefined(cacheData[i]) &&
!config.compare(cacheData[i], pageData))
if (fn && shouldFetchPage) {
if (
fn &&
(typeof shouldRevalidatePage === 'function'
? shouldRevalidatePage(pageData, pageArg)
: shouldFetchPage)
) {
const revalidate = async () => {
const hasPreloadedRequest = pageKey in PRELOAD
if (!hasPreloadedRequest) {
Expand Down Expand Up @@ -238,7 +245,7 @@ export const infinite = (<Data, Error>(useSWRNext: SWRHook) =>
| Data[]
| Promise<Data[] | undefined>
| MutatorCallback<Data[]>,
opts?: undefined | boolean | MutatorOptions<Data[], T>
opts?: undefined | boolean | SWRInfiniteMutatorOptions<Data[], T>
) {
// When passing as a boolean, it's explicitly used to disable/enable
// revalidation.
Expand All @@ -253,10 +260,10 @@ export const infinite = (<Data, Error>(useSWRNext: SWRHook) =>
if (shouldRevalidate) {
if (!isUndefined(data)) {
// We only revalidate the pages that are changed
set({ _i: false })
set({ _i: false, _r: options.revalidate })
} else {
// Calling `mutate()`, we revalidate all pages
set({ _i: true })
set({ _i: true, _r: options.revalidate })
}
}

Expand Down
24 changes: 22 additions & 2 deletions src/infinite/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import type {
Arguments,
BareFetcher,
State,
StrictTupleKey
StrictTupleKey,
MutatorOptions,
MutatorCallback
} from '../_internal'

type FetcherResponse<Data = unknown> = Data | Promise<Data>
Expand Down Expand Up @@ -41,12 +43,29 @@ export interface SWRInfiniteConfiguration<
compare?: SWRInfiniteCompareFn<Data>
}

interface SWRInfiniteRevalidateFn<Data = any> {
(data: Data, key: Arguments): boolean
}

type InfiniteKeyedMutator<Data> = <MutationData = Data>(
data?: Data | Promise<Data | undefined> | MutatorCallback<Data>,
opts?: boolean | SWRInfiniteMutatorOptions<Data, MutationData>
) => Promise<Data | MutationData | undefined>

export interface SWRInfiniteMutatorOptions<Data = any, MutationData = Data>
extends Omit<MutatorOptions<Data, MutationData>, 'revalidate'> {
revalidate?:
| boolean
| SWRInfiniteRevalidateFn<Data extends unknown[] ? Data[number] : never>
}

export interface SWRInfiniteResponse<Data = any, Error = any>
extends SWRResponse<Data[], Error> {
extends Omit<SWRResponse<Data[], Error>, 'mutate'> {
size: number
setSize: (
size: number | ((_size: number) => number)
) => Promise<Data[] | undefined>
mutate: InfiniteKeyedMutator<Data[]>
}

export interface SWRInfiniteHook {
Expand Down Expand Up @@ -134,4 +153,5 @@ export interface SWRInfiniteCacheValue<Data = any, Error = any>
// same key.
_l?: number
_k?: Arguments
_r?: boolean | SWRInfiniteRevalidateFn
}
4 changes: 2 additions & 2 deletions src/mutation/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { SWRResponse, Key } from '../core'
import type { SWRResponse, Key, Arguments } from '../core'

type FetcherResponse<Data> = Data | Promise<Data>

Expand All @@ -25,7 +25,7 @@ export type SWRMutationConfiguration<
ExtraArg = any,
SWRData = any
> = {
revalidate?: boolean
revalidate?: boolean | ((data: Data, key: Arguments) => boolean)
populateCache?:
| boolean
| ((result: Data, currentData: SWRData | undefined) => SWRData)
Expand Down
33 changes: 33 additions & 0 deletions test/type/config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,36 @@ export function testFallbackDataConfig() {
expectType<Equal<typeof data, { value: string }>>(true)
expectType<Equal<typeof isLoading, boolean>>(true)
}

export function testProviderConfig() {
const GlobalSetting = ({ children }: { children: React.ReactNode }) => {
return (
<SWRConfig
value={{
provider: () => new Map(),
isOnline() {
/* Customize the network state detector */
return true
},
isVisible() {
/* Customize the visibility state detector */
return true
},
initFocus(_callback) {
/* Register the listener with your state provider */
},
initReconnect(_callback) {
/* Register the listener with your state provider */
}
}}
>
{children}
</SWRConfig>
)
}
return (
<GlobalSetting>
<div />
</GlobalSetting>
)
}
44 changes: 44 additions & 0 deletions test/use-swr-infinite.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1843,4 +1843,48 @@ describe('useSWRInfinite', () => {
screen.getByText('data:apple, banana, pineapple,')
expect(previousPageDataLogs.every(d => d === null)).toBeTruthy()
})

it('should support revalidate as a function', async () => {
// mock api
let pageData = ['apple', 'banana', 'pineapple']

const key = createKey()
function Page() {
const { data, mutate: boundMutate } = useSWRInfinite(
index => [key, index],
([_, index]) => createResponse(pageData[index]),
{
initialSize: 3
}
)

return (
<div
onClick={() => {
boundMutate(undefined, {
// only revalidate 'apple' & 'pineapple' (page=2)
revalidate: (d, [_, i]: [string, number]) => {
return d === 'apple' || i === 2
}
})
}}
>
data:{Array.isArray(data) && data.join(',')}
</div>
)
}

renderWithConfig(<Page />)
screen.getByText('data:')

await screen.findByText('data:apple,banana,pineapple')

// update response data
pageData = pageData.map(data => `[${data}]`)

// revalidate
fireEvent.click(screen.getByText('data:apple,banana,pineapple'))

await screen.findByText('data:[apple],banana,[pineapple]')
})
})
Loading

0 comments on commit 58af2ad

Please sign in to comment.