diff --git a/src/cache.ts b/src/cache.ts index 027ab1b03..8561f5af8 100644 --- a/src/cache.ts +++ b/src/cache.ts @@ -1,21 +1,21 @@ -import { CacheInterface, keyInterface, cacheListener } from './types' +import { Cache as CacheType, Key, CacheListener } from './types' import hash from './libs/hash' -export default class Cache implements CacheInterface { +export default class Cache implements CacheType { private __cache: Map - private __listeners: cacheListener[] + private __listeners: CacheListener[] constructor(initialData: any = {}) { this.__cache = new Map(Object.entries(initialData)) this.__listeners = [] } - get(key: keyInterface): any { + get(key: Key): any { const [_key] = this.serializeKey(key) return this.__cache.get(_key) } - set(key: keyInterface, value: any): any { + set(key: Key, value: any): any { const [_key] = this.serializeKey(key) this.__cache.set(_key, value) this.notify() @@ -25,7 +25,7 @@ export default class Cache implements CacheInterface { return Array.from(this.__cache.keys()) } - has(key: keyInterface) { + has(key: Key) { const [_key] = this.serializeKey(key) return this.__cache.has(_key) } @@ -35,14 +35,14 @@ export default class Cache implements CacheInterface { this.notify() } - delete(key: keyInterface) { + delete(key: Key) { const [_key] = this.serializeKey(key) this.__cache.delete(_key) this.notify() } // TODO: introduce namespace for the cache - serializeKey(key: keyInterface): [string, any, string, string] { + serializeKey(key: Key): [string, any, string, string] { let args = null if (typeof key === 'function') { try { @@ -68,7 +68,7 @@ export default class Cache implements CacheInterface { return [key, args, errorKey, isValidatingKey] } - subscribe(listener: cacheListener) { + subscribe(listener: CacheListener) { if (typeof listener !== 'function') { throw new Error('Expected the listener to be a function.') } diff --git a/src/config.ts b/src/config.ts index 3d7c8c74c..31cc5dec7 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,9 +1,5 @@ import { dequal } from 'dequal/lite' -import { - ConfigInterface, - RevalidateOptionInterface, - revalidateType -} from './types' +import { SWRConfiguration, RevalidatorOptions, Revalidator } from './types' import Cache from './cache' import webPreset from './libs/web-preset' @@ -14,9 +10,9 @@ const cache = new Cache() function onErrorRetry( _, __, - config: ConfigInterface, - revalidate: revalidateType, - opts: RevalidateOptionInterface + config: SWRConfiguration, + revalidate: Revalidator, + opts: RevalidatorOptions ): void { if (!config.isDocumentVisible()) { // if it's hidden, stop @@ -47,7 +43,7 @@ const slowConnection = ['slow-2g', '2g'].indexOf(navigator['connection'].effectiveType) !== -1 // config -const defaultConfig: ConfigInterface = Object.assign( +const defaultConfig: SWRConfiguration = Object.assign( { // events onLoadingSlow: () => {}, diff --git a/src/index.ts b/src/index.ts index 44daca680..fa36ab725 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,17 +1,31 @@ -export * from './use-swr' +// `useSWR` and related APIs import { default as useSWR } from './use-swr' -export { - useSWRInfinite, - SWRInfiniteConfigInterface, - SWRInfiniteResponseInterface -} from './use-swr-infinite' +export default useSWR +export * from './use-swr' + +// `useSWRInfinite` +export { useSWRInfinite } from './use-swr-infinite' + +// Cache related, to be replaced by the new APIs export { cache } from './config' + +// Types export { + SWRConfiguration, + SWRInfiniteConfiguration, + SWRInfiniteResponse, + Revalidator, + RevalidatorOptions, + Key, + SWRResponse, + Cache, + // Legacy, for backwards compatibility ConfigInterface, + SWRInfiniteConfigInterface, + SWRInfiniteResponseInterface, revalidateType, RevalidateOptionInterface, keyInterface, responseInterface, CacheInterface } from './types' -export default useSWR diff --git a/src/swr-config-context.ts b/src/swr-config-context.ts index f3d2d1e7a..05fa8f625 100644 --- a/src/swr-config-context.ts +++ b/src/swr-config-context.ts @@ -1,8 +1,8 @@ import { createContext } from 'react' -import { ConfigInterface } from './types' +import { SWRConfiguration } from './types' -const SWRConfigContext = createContext>({}) +const SWRConfigContext = createContext({}) SWRConfigContext.displayName = 'SWRConfigContext' export default SWRConfigContext diff --git a/src/types.ts b/src/types.ts index 86aef5875..b31c86d1c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,9 +1,12 @@ -export type fetcherFn = (...args: any) => Data | Promise -export interface ConfigInterface< +// Internal types + +export type Fetcher = (...args: any) => Data | Promise + +export type Configuration< Data = any, Error = any, - Fn extends fetcherFn = fetcherFn -> { + Fn extends Fetcher = Fetcher +> = { errorRetryInterval: number errorRetryCount?: number loadingTimeout: number @@ -16,30 +19,26 @@ export interface ConfigInterface< revalidateOnMount?: boolean revalidateOnReconnect: boolean shouldRetryOnError: boolean - fetcher: Fn suspense: boolean + fetcher: Fn initialData?: Data isOnline: () => boolean isDocumentVisible: () => boolean isPaused: () => boolean - onLoadingSlow: (key: string, config: ConfigInterface) => void + onLoadingSlow: (key: string, config: Configuration) => void onSuccess: ( data: Data, key: string, - config: ConfigInterface - ) => void - onError: ( - err: Error, - key: string, - config: ConfigInterface + config: Configuration ) => void + onError: (err: Error, key: string, config: Configuration) => void onErrorRetry: ( err: Error, key: string, - config: ConfigInterface, - revalidate: revalidateType, - revalidateOpts: RevalidateOptionInterface + config: Configuration, + revalidate: Revalidator, + revalidateOpts: RevalidatorOptions ) => void registerOnFocus?: (cb: () => void) => void registerOnReconnect?: (cb: () => void) => void @@ -47,68 +46,171 @@ export interface ConfigInterface< compare: (a: Data | undefined, b: Data | undefined) => boolean } -export interface RevalidateOptionInterface { - retryCount?: number - dedupe?: boolean -} +export type ValueKey = string | any[] | null -export type keyType = string | any[] | null -type keyFunction = () => keyType -export type keyInterface = keyFunction | keyType -export type updaterInterface = ( +export type Updater = ( shouldRevalidate?: boolean, data?: Data, error?: Error, shouldDedupe?: boolean, dedupe?: boolean ) => boolean | Promise -export type triggerInterface = ( - key: keyInterface, - shouldRevalidate?: boolean -) => Promise -export type mutateCallback = ( +export type Trigger = (key: Key, shouldRevalidate?: boolean) => Promise + +type MutatorCallback = ( currentValue: undefined | Data ) => Promise | undefined | Data -export type mutateInterface = ( - key: keyInterface, - data?: Data | Promise | mutateCallback, + +export type Mutator = ( + key: Key, + data?: Data | Promise | MutatorCallback, shouldRevalidate?: boolean ) => Promise -export type broadcastStateInterface = ( +export type Broadcaster = ( key: string, data: Data, error?: Error, isValidating?: boolean ) => void + +export type Action = { + data?: Data + error?: Error + isValidating?: boolean +} + +export type CacheListener = () => void + +// Public types + +/** + * @deprecated `ConfigInterface` will be renamed to `SWRConfiguration`. + */ +export type ConfigInterface< + Data = any, + Error = any, + Fn extends Fetcher = Fetcher +> = Partial> +export type SWRConfiguration< + Data = any, + Error = any, + Fn extends Fetcher = Fetcher +> = Partial> + +/** + * @deprecated `keyInterface` will be renamed to `Key`. + */ +export type keyInterface = ValueKey | (() => ValueKey) +export type Key = ValueKey | (() => ValueKey) + +/** + * @deprecated `responseInterface` will be renamed to `SWRResponse`. + */ export type responseInterface = { data?: Data error?: Error revalidate: () => Promise mutate: ( - data?: Data | Promise | mutateCallback, + data?: Data | Promise | MutatorCallback, shouldRevalidate?: boolean ) => Promise isValidating: boolean } -export type revalidateType = ( - revalidateOpts: RevalidateOptionInterface -) => Promise - -export type actionType = { +export type SWRResponse = { data?: Data error?: Error - isValidating?: boolean + revalidate: () => Promise + mutate: ( + data?: Data | Promise | MutatorCallback, + shouldRevalidate?: boolean + ) => Promise + isValidating: boolean +} + +/** + * @deprecated `SWRInfiniteConfigInterface` will be renamed to `SWRInfiniteConfiguration`. + */ +export type SWRInfiniteConfigInterface< + Data = any, + Error = any +> = SWRConfiguration> & { + initialSize?: number + revalidateAll?: boolean + persistSize?: boolean +} +export type SWRInfiniteConfiguration< + Data = any, + Error = any +> = SWRConfiguration> & { + initialSize?: number + revalidateAll?: boolean + persistSize?: boolean +} + +/** + * @deprecated `SWRInfiniteResponseInterface` will be renamed to `SWRInfiniteResponse`. + */ +export type SWRInfiniteResponseInterface = SWRResponse< + Data[], + Error +> & { + size: number + setSize: ( + size: number | ((size: number) => number) + ) => Promise +} +export type SWRInfiniteResponse = SWRResponse< + Data[], + Error +> & { + size: number + setSize: ( + size: number | ((size: number) => number) + ) => Promise +} + +/** + * @deprecated `RevalidateOptionInterface` will be renamed to `RevalidatorOptions`. + */ +export interface RevalidateOptionInterface { + retryCount?: number + dedupe?: boolean +} +export interface RevalidatorOptions { + retryCount?: number + dedupe?: boolean } +/** + * @deprecated `revalidateType` will be renamed to `Revalidator`. + */ +export type revalidateType = ( + revalidateOpts: RevalidatorOptions +) => Promise +export type Revalidator = ( + revalidateOpts: RevalidatorOptions +) => Promise + +/** + * @deprecated `CacheInterface` will be renamed to `Cache`. + */ export interface CacheInterface { - get(key: keyInterface): any - set(key: keyInterface, value: any): any + get(key: Key): any + set(key: Key, value: any): any keys(): string[] - has(key: keyInterface): boolean - delete(key: keyInterface): void + has(key: Key): boolean + delete(key: Key): void clear(): void - serializeKey(key: keyInterface): [string, any, string, string] - subscribe(listener: cacheListener): () => void + serializeKey(key: Key): [string, any, string, string] + subscribe(listener: CacheListener): () => void +} +export interface Cache { + get(key: Key): any + set(key: Key, value: any): any + keys(): string[] + has(key: Key): boolean + delete(key: Key): void + clear(): void + serializeKey(key: Key): [string, any, string, string] + subscribe(listener: CacheListener): () => void } - -export type cacheListener = () => void diff --git a/src/use-swr-infinite.ts b/src/use-swr-infinite.ts index 63d8b88bc..e0277cd08 100644 --- a/src/use-swr-infinite.ts +++ b/src/use-swr-infinite.ts @@ -4,49 +4,36 @@ import defaultConfig, { cache } from './config' import SWRConfigContext from './swr-config-context' import useSWR from './use-swr' -import { keyType, fetcherFn, ConfigInterface, responseInterface } from './types' +import { + ValueKey, + Fetcher, + SWRInfiniteConfiguration, + SWRInfiniteResponse +} from './types' type KeyLoader = ( index: number, previousPageData: Data | null -) => keyType -type SWRInfiniteConfigInterface = ConfigInterface< - Data[], - Error, - fetcherFn -> & { - initialSize?: number - revalidateAll?: boolean - persistSize?: boolean -} -type SWRInfiniteResponseInterface = responseInterface< - Data[], - Error -> & { - size: number - setSize: ( - size: number | ((size: number) => number) - ) => Promise -} +) => ValueKey function useSWRInfinite( getKey: KeyLoader -): SWRInfiniteResponseInterface +): SWRInfiniteResponse function useSWRInfinite( getKey: KeyLoader, - config?: Partial> -): SWRInfiniteResponseInterface + config?: Partial> +): SWRInfiniteResponse function useSWRInfinite( getKey: KeyLoader, - fn?: fetcherFn, - config?: Partial> -): SWRInfiniteResponseInterface + fn?: Fetcher, + config?: Partial> +): SWRInfiniteResponse function useSWRInfinite( getKey: KeyLoader, ...options: any[] -): SWRInfiniteResponseInterface { - let _fn: fetcherFn | undefined, - _config: Partial> = {} +): SWRInfiniteResponse { + let _fn: Fetcher | undefined, + _config: Partial> = {} if (options.length > 1) { _fn = options[0] @@ -59,7 +46,7 @@ function useSWRInfinite( } } - const config: SWRInfiniteConfigInterface = Object.assign( + const config: SWRInfiniteConfiguration = Object.assign( {}, defaultConfig, useContext(SWRConfigContext), @@ -241,11 +228,7 @@ function useSWRInfinite( enumerable: true } }) - return swrInfinite as SWRInfiniteResponseInterface + return swrInfinite as SWRInfiniteResponse } -export { - useSWRInfinite, - SWRInfiniteConfigInterface, - SWRInfiniteResponseInterface -} +export { useSWRInfinite } diff --git a/src/use-swr.ts b/src/use-swr.ts index 5847e8d72..c065960e2 100644 --- a/src/use-swr.ts +++ b/src/use-swr.ts @@ -12,16 +12,17 @@ import { import defaultConfig, { cache } from './config' import SWRConfigContext from './swr-config-context' import { - actionType, - broadcastStateInterface, - ConfigInterface, - fetcherFn, - keyInterface, - mutateInterface, - responseInterface, - RevalidateOptionInterface, - triggerInterface, - updaterInterface + Action, + Broadcaster, + Configuration, + SWRConfiguration, + Fetcher, + Key, + Mutator, + SWRResponse, + RevalidatorOptions, + Trigger, + Updater } from './types' const IS_SERVER = @@ -73,7 +74,7 @@ if (!IS_SERVER) { } } -const trigger: triggerInterface = (_key, shouldRevalidate = true) => { +const trigger: Trigger = (_key, shouldRevalidate = true) => { // we are ignoring the second argument which correspond to the arguments // the fetcher will receive when key is an array const [key, , keyErr, keyValidating] = cache.serializeKey(_key) @@ -103,12 +104,7 @@ const trigger: triggerInterface = (_key, shouldRevalidate = true) => { return Promise.resolve(cache.get(key)) } -const broadcastState: broadcastStateInterface = ( - key, - data, - error, - isValidating -) => { +const broadcastState: Broadcaster = (key, data, error, isValidating) => { const updaters = CACHE_REVALIDATORS[key] if (key && updaters) { for (let i = 0; i < updaters.length; ++i) { @@ -117,11 +113,7 @@ const broadcastState: broadcastStateInterface = ( } } -const mutate: mutateInterface = async ( - _key, - _data, - shouldRevalidate = true -) => { +const mutate: Mutator = async (_key, _data, shouldRevalidate = true) => { const [key, , keyErr] = cache.serializeKey(_key) if (!key) return @@ -212,26 +204,24 @@ const mutate: mutateInterface = async ( return data } +function useSWR(key: Key): SWRResponse function useSWR( - key: keyInterface -): responseInterface + key: Key, + config?: SWRConfiguration +): SWRResponse function useSWR( - key: keyInterface, - config?: Partial> -): responseInterface -function useSWR( - key: keyInterface, + key: Key, // `null` is used for a hack to manage shared state with SWR // https://github.com/vercel/swr/pull/918 - fn?: fetcherFn | null, - config?: Partial> -): responseInterface + fn?: Fetcher | null, + config?: SWRConfiguration +): SWRResponse function useSWR( - _key: keyInterface, + _key: Key, ...options: any[] -): responseInterface { - let _fn: fetcherFn | undefined, - _config: Partial> = {} +): SWRResponse { + let _fn: Fetcher | undefined, + _config: SWRConfiguration = {} if (options.length > 1) { _fn = options[0] _config = options[1] @@ -249,12 +239,12 @@ function useSWR( // `keyErr` is the cache key for error objects const [key, fnArgs, keyErr, keyValidating] = cache.serializeKey(_key) - const config: ConfigInterface = Object.assign( + const config = Object.assign( {}, defaultConfig, useContext(SWRConfigContext), _config - ) + ) as Configuration const configRef = useRef(config) useIsomorphicLayoutEffect(() => { @@ -302,7 +292,7 @@ function useSWR( const [, rerender] = useState(null) let dispatch = useCallback( - (payload: actionType) => { + (payload: Action) => { let shouldUpdateState = false for (let k in payload) { if (stateRef.current[k] === payload[k]) { @@ -345,7 +335,7 @@ function useSWR( [key] ) - const boundMutate: responseInterface['mutate'] = useCallback( + const boundMutate: SWRResponse['mutate'] = useCallback( (data, shouldRevalidate) => { return mutate(keyRef.current, data, shouldRevalidate) }, @@ -376,9 +366,7 @@ function useSWR( // start a revalidation const revalidate = useCallback( - async ( - revalidateOpts: RevalidateOptionInterface = {} - ): Promise => { + async (revalidateOpts: RevalidatorOptions = {}): Promise => { if (!key || !fn) return false if (unmountedRef.current) return false if (configRef.current.isPaused()) return false @@ -479,7 +467,7 @@ function useSWR( cache.set(keyValidating, false) // new state for the reducer - const newState: actionType = { + const newState: Action = { isValidating: false } @@ -616,7 +604,7 @@ function useSWR( } // register global cache update listener - const onUpdate: updaterInterface = ( + const onUpdate: Updater = ( shouldRevalidate = true, updatedData, updatedError, @@ -624,7 +612,7 @@ function useSWR( dedupe = true ) => { // update hook state - const newState: actionType = {} + const newState: Action = {} let needUpdate = false if ( @@ -770,7 +758,7 @@ function useSWR( // revalidate will be deprecated in the 1.x release // because mutate() covers the same use case of revalidate(). // This remains only for backward compatibility - const state = { revalidate, mutate: boundMutate } as responseInterface< + const state = { revalidate, mutate: boundMutate } as SWRResponse< Data, Error >