Initial commit
This commit is contained in:
commit
afed023152
8152 files changed
+2086461
No files matched your search
+3
@@ -0,0 +1,3 @@
|
||||
declare const INFINITE_PREFIX = "$inf$";
|
||||
|
||||
export { INFINITE_PREFIX };
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
declare const INFINITE_PREFIX = "$inf$";
|
||||
|
||||
export { INFINITE_PREFIX };
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
const INFINITE_PREFIX = '$inf$';
|
||||
|
||||
exports.INFINITE_PREFIX = INFINITE_PREFIX;
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
const INFINITE_PREFIX = '$inf$';
|
||||
|
||||
export { INFINITE_PREFIX };
|
||||
+1
@@ -0,0 +1 @@
|
||||
export { E as ERROR_REVALIDATE_EVENT, F as FOCUS_EVENT, M as MUTATE_EVENT, R as RECONNECT_EVENT, U as UNLOAD_EVENT } from '../events-ny18dyc2.js';
|
||||
+1
@@ -0,0 +1 @@
|
||||
export { E as ERROR_REVALIDATE_EVENT, F as FOCUS_EVENT, M as MUTATE_EVENT, R as RECONNECT_EVENT, U as UNLOAD_EVENT } from '../events-ny18dyc2.js';
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
var bunchee_group_0 = require('../config-context-s960zqob.js');
|
||||
|
||||
|
||||
|
||||
exports.ERROR_REVALIDATE_EVENT = bunchee_group_0.ERROR_REVALIDATE_EVENT;
|
||||
exports.FOCUS_EVENT = bunchee_group_0.FOCUS_EVENT;
|
||||
exports.MUTATE_EVENT = bunchee_group_0.MUTATE_EVENT;
|
||||
exports.RECONNECT_EVENT = bunchee_group_0.RECONNECT_EVENT;
|
||||
exports.UNLOAD_EVENT = bunchee_group_0.UNLOAD_EVENT;
|
||||
+1
@@ -0,0 +1 @@
|
||||
export { E as ERROR_REVALIDATE_EVENT, F as FOCUS_EVENT, M as MUTATE_EVENT, R as RECONNECT_EVENT, h as UNLOAD_EVENT } from '../config-context-ext53wcz.mjs';
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
import { h as Cache, P as ProviderConfiguration, i as ScopedMutator, U as Unloader, G as GlobalState, j as State, k as FullConfiguration, A as Arguments, d as MutatorCallback, g as MutatorOptions, K as Key, l as Fetcher, e as SWRConfiguration, a as SWRHook, M as Middleware } from '../types-l2s7dphv.js';
|
||||
export { B as BareFetcher, m as BlockingData, n as Broadcaster, C as CacheData, F as FetcherResponse, I as InternalConfiguration, o as IsLoadingResponse, p as KeyLoader, q as KeyedMutator, r as Mutator, s as MutatorConfig, t as MutatorFn, v as MutatorWrapper, w as PublicConfiguration, R as RevalidateCallback, x as RevalidateEvent, y as Revalidator, z as RevalidatorOptions, S as SWRConfig, b as SWRConfigValue, c as SWRResponse, D as StateDependencies, f as StrictTupleKey, E as UnloadOptions, H as cache, J as compare, L as defaultConfig, N as mutate, O as preload, Q as unload, T as useSWRConfig } from '../types-l2s7dphv.js';
|
||||
export { e as revalidateEvents } from '../events-ny18dyc2.js';
|
||||
export { INFINITE_PREFIX } from './constants.mjs';
|
||||
import react__default from 'react';
|
||||
export { s as serialize } from '../serialize-bwklnvdf.js';
|
||||
|
||||
declare const initCache: <Data = any>(provider: Cache<Data>, options?: Partial<ProviderConfiguration>) => [Cache<Data>, ScopedMutator, () => void, () => void, Unloader] | [Cache<Data>, ScopedMutator, undefined, undefined, Unloader] | undefined;
|
||||
|
||||
declare const IS_REACT_LEGACY = false;
|
||||
declare const IS_SERVER: boolean;
|
||||
declare const rAF: (f: (...args: any[]) => void) => number | ReturnType<typeof setTimeout>;
|
||||
declare const useIsomorphicLayoutEffect: typeof react__default.useEffect;
|
||||
declare const slowConnection: boolean | undefined;
|
||||
|
||||
declare const SWRGlobalState: WeakMap<Cache<any>, GlobalState>;
|
||||
|
||||
declare const stableHash: (arg: any) => string;
|
||||
|
||||
declare const isWindowDefined: boolean;
|
||||
declare const isDocumentDefined: boolean;
|
||||
declare const isLegacyDeno: boolean;
|
||||
declare const hasRequestAnimationFrame: () => boolean;
|
||||
declare const createCacheHelper: <Data = any, T = State<Data, any>>(cache: Cache, key: string | undefined) => readonly [() => T, (info: T) => void, (key: string, callback: (current: any, prev: any) => void) => () => void, () => any];
|
||||
|
||||
declare const noop: () => void;
|
||||
declare const UNDEFINED: undefined;
|
||||
declare const OBJECT: ObjectConstructor;
|
||||
declare const isUndefined: (v: any) => v is undefined;
|
||||
declare const isFunction: <T extends (...args: any[]) => any = (...args: any[]) => any>(v: unknown) => v is T;
|
||||
declare const mergeObjects: (a: any, b?: any) => any;
|
||||
declare const isPromiseLike: (x: unknown) => x is PromiseLike<unknown>;
|
||||
|
||||
declare const mergeConfigs: (a: Partial<FullConfiguration>, b?: Partial<FullConfiguration>) => Partial<FullConfiguration>;
|
||||
|
||||
type KeyFilter = (key?: Arguments) => boolean;
|
||||
declare function internalMutate<Data>(cache: Cache, _key: KeyFilter, _data?: Data | Promise<Data | undefined> | MutatorCallback<Data>, _opts?: boolean | MutatorOptions<Data>): Promise<Array<Data | undefined>>;
|
||||
declare function internalMutate<Data>(cache: Cache, _key: Arguments, _data?: Data | Promise<Data | undefined> | MutatorCallback<Data>, _opts?: boolean | MutatorOptions<Data>): Promise<Data | undefined>;
|
||||
|
||||
declare const normalize: <KeyType = Key, Data = any>(args: [KeyType] | [KeyType, Fetcher<Data> | null] | [KeyType, SWRConfiguration | undefined] | [KeyType, Fetcher<Data> | null, SWRConfiguration | undefined]) => [KeyType, Fetcher<Data> | null, Partial<SWRConfiguration<Data>>];
|
||||
|
||||
declare const withArgs: <SWRType>(hook: any) => SWRType;
|
||||
|
||||
type Callback = (...args: any[]) => any;
|
||||
declare const subscribeCallback: (key: string, callbacks: Record<string, Callback[]>, callback: Callback) => () => void;
|
||||
|
||||
declare const getTimestamp: () => number;
|
||||
|
||||
declare const preset: {
|
||||
readonly isOnline: () => boolean;
|
||||
readonly isVisible: () => boolean;
|
||||
};
|
||||
declare const defaultConfigOptions: ProviderConfiguration;
|
||||
|
||||
declare const withMiddleware: (useSWR: SWRHook, middleware: Middleware) => SWRHook;
|
||||
|
||||
export { Arguments, Cache, Fetcher, FullConfiguration, GlobalState, IS_REACT_LEGACY, IS_SERVER, Key, Middleware, MutatorCallback, MutatorOptions, OBJECT, ProviderConfiguration, SWRConfiguration, SWRGlobalState, SWRHook, ScopedMutator, State, UNDEFINED, Unloader, createCacheHelper, defaultConfigOptions, getTimestamp, hasRequestAnimationFrame, initCache, internalMutate, isDocumentDefined, isFunction, isLegacyDeno, isPromiseLike, isUndefined, isWindowDefined, mergeConfigs, mergeObjects, noop, normalize, preset, rAF, slowConnection, stableHash, subscribeCallback, useIsomorphicLayoutEffect, withArgs, withMiddleware };
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
import { a as Cache, P as ProviderConfiguration, b as ScopedMutator, U as Unloader, G as GlobalState, c as State, d as FullConfiguration, A as Arguments, M as MutatorCallback, e as MutatorOptions, K as Key, f as Fetcher, g as SWRConfiguration, h as SWRHook, i as Middleware } from '../types-m1fld6v2.js';
|
||||
export { B as BareFetcher, j as BlockingData, k as Broadcaster, C as CacheData, F as FetcherResponse, I as InternalConfiguration, l as IsLoadingResponse, m as KeyLoader, n as KeyedMutator, o as Mutator, p as MutatorConfig, q as MutatorFn, r as MutatorWrapper, s as PublicConfiguration, R as RevalidateCallback, t as RevalidateEvent, u as Revalidator, v as RevalidatorOptions, S as SWRConfig, w as SWRConfigValue, x as SWRResponse, y as StateDependencies, z as StrictTupleKey, D as UnloadOptions, E as cache, H as compare, J as defaultConfig, L as mutate, N as preload, O as unload, Q as useSWRConfig } from '../types-m1fld6v2.js';
|
||||
export { e as revalidateEvents } from '../events-ny18dyc2.js';
|
||||
export { INFINITE_PREFIX } from './constants.js';
|
||||
import react from 'react';
|
||||
export { s as serialize } from '../serialize-f94ud86v.js';
|
||||
|
||||
declare const initCache: <Data = any>(provider: Cache<Data>, options?: Partial<ProviderConfiguration>) => [Cache<Data>, ScopedMutator, () => void, () => void, Unloader] | [Cache<Data>, ScopedMutator, undefined, undefined, Unloader] | undefined;
|
||||
|
||||
declare const IS_REACT_LEGACY = false;
|
||||
declare const IS_SERVER: boolean;
|
||||
declare const rAF: (f: (...args: any[]) => void) => number | ReturnType<typeof setTimeout>;
|
||||
declare const useIsomorphicLayoutEffect: typeof react.useEffect;
|
||||
declare const slowConnection: boolean | undefined;
|
||||
|
||||
declare const SWRGlobalState: WeakMap<Cache<any>, GlobalState>;
|
||||
|
||||
declare const stableHash: (arg: any) => string;
|
||||
|
||||
declare const isWindowDefined: boolean;
|
||||
declare const isDocumentDefined: boolean;
|
||||
declare const isLegacyDeno: boolean;
|
||||
declare const hasRequestAnimationFrame: () => boolean;
|
||||
declare const createCacheHelper: <Data = any, T = State<Data, any>>(cache: Cache, key: string | undefined) => readonly [() => T, (info: T) => void, (key: string, callback: (current: any, prev: any) => void) => () => void, () => any];
|
||||
|
||||
declare const noop: () => void;
|
||||
declare const UNDEFINED: undefined;
|
||||
declare const OBJECT: ObjectConstructor;
|
||||
declare const isUndefined: (v: any) => v is undefined;
|
||||
declare const isFunction: <T extends (...args: any[]) => any = (...args: any[]) => any>(v: unknown) => v is T;
|
||||
declare const mergeObjects: (a: any, b?: any) => any;
|
||||
declare const isPromiseLike: (x: unknown) => x is PromiseLike<unknown>;
|
||||
|
||||
declare const mergeConfigs: (a: Partial<FullConfiguration>, b?: Partial<FullConfiguration>) => Partial<FullConfiguration>;
|
||||
|
||||
type KeyFilter = (key?: Arguments) => boolean;
|
||||
declare function internalMutate<Data>(cache: Cache, _key: KeyFilter, _data?: Data | Promise<Data | undefined> | MutatorCallback<Data>, _opts?: boolean | MutatorOptions<Data>): Promise<Array<Data | undefined>>;
|
||||
declare function internalMutate<Data>(cache: Cache, _key: Arguments, _data?: Data | Promise<Data | undefined> | MutatorCallback<Data>, _opts?: boolean | MutatorOptions<Data>): Promise<Data | undefined>;
|
||||
|
||||
declare const normalize: <KeyType = Key, Data = any>(args: [KeyType] | [KeyType, Fetcher<Data> | null] | [KeyType, SWRConfiguration | undefined] | [KeyType, Fetcher<Data> | null, SWRConfiguration | undefined]) => [KeyType, Fetcher<Data> | null, Partial<SWRConfiguration<Data>>];
|
||||
|
||||
declare const withArgs: <SWRType>(hook: any) => SWRType;
|
||||
|
||||
type Callback = (...args: any[]) => any;
|
||||
declare const subscribeCallback: (key: string, callbacks: Record<string, Callback[]>, callback: Callback) => () => void;
|
||||
|
||||
declare const getTimestamp: () => number;
|
||||
|
||||
declare const preset: {
|
||||
readonly isOnline: () => boolean;
|
||||
readonly isVisible: () => boolean;
|
||||
};
|
||||
declare const defaultConfigOptions: ProviderConfiguration;
|
||||
|
||||
declare const withMiddleware: (useSWR: SWRHook, middleware: Middleware) => SWRHook;
|
||||
|
||||
export { Arguments, Cache, Fetcher, FullConfiguration, GlobalState, IS_REACT_LEGACY, IS_SERVER, Key, Middleware, MutatorCallback, MutatorOptions, OBJECT, ProviderConfiguration, SWRConfiguration, SWRGlobalState, SWRHook, ScopedMutator, State, UNDEFINED, Unloader, createCacheHelper, defaultConfigOptions, getTimestamp, hasRequestAnimationFrame, initCache, internalMutate, isDocumentDefined, isFunction, isLegacyDeno, isPromiseLike, isUndefined, isWindowDefined, mergeConfigs, mergeObjects, noop, normalize, preset, rAF, slowConnection, stableHash, subscribeCallback, useIsomorphicLayoutEffect, withArgs, withMiddleware };
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
var bunchee_group_0 = require('../config-context-s960zqob.js');
|
||||
var _internal_constants = require('./constants.js');
|
||||
var React = require('react');
|
||||
|
||||
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
||||
|
||||
var React__default = /*#__PURE__*/_interopDefault(React);
|
||||
|
||||
// @ts-expect-error
|
||||
const enableDevtools = bunchee_group_0.isWindowDefined && window.__SWR_DEVTOOLS_USE__;
|
||||
const use = enableDevtools ? window.__SWR_DEVTOOLS_USE__ : [];
|
||||
const setupDevTools = ()=>{
|
||||
if (enableDevtools) {
|
||||
// @ts-expect-error
|
||||
window.__SWR_DEVTOOLS_REACT__ = React__default.default;
|
||||
}
|
||||
};
|
||||
|
||||
const normalize = (args)=>{
|
||||
return bunchee_group_0.isFunction(args[1]) ? [
|
||||
args[0],
|
||||
args[1],
|
||||
args[2] || {}
|
||||
] : [
|
||||
args[0],
|
||||
null,
|
||||
(args[1] === null ? args[2] : args[1]) || {}
|
||||
];
|
||||
};
|
||||
|
||||
const useSWRConfig = ()=>{
|
||||
const parentConfig = React.useContext(bunchee_group_0.SWRConfigContext);
|
||||
const mergedConfig = React.useMemo(()=>bunchee_group_0.mergeObjects(bunchee_group_0.defaultConfig, parentConfig), [
|
||||
parentConfig
|
||||
]);
|
||||
return mergedConfig;
|
||||
};
|
||||
|
||||
const preload = (key_, fetcher)=>{
|
||||
// preload should be a no-op on the server
|
||||
if (bunchee_group_0.IS_SERVER) {
|
||||
return undefined;
|
||||
}
|
||||
const [key, fnArg] = bunchee_group_0.serialize(key_);
|
||||
const [, , , PRELOAD] = bunchee_group_0.SWRGlobalState.get(bunchee_group_0.cache);
|
||||
// Prevent preload to be called multiple times before used.
|
||||
if (PRELOAD[key]) {
|
||||
return PRELOAD[key];
|
||||
}
|
||||
const req = fetcher(fnArg);
|
||||
PRELOAD[key] = req;
|
||||
return req;
|
||||
};
|
||||
const middleware = (useSWRNext)=>(key_, fetcher_, config)=>{
|
||||
// fetcher might be a sync function, so this should not be an async function
|
||||
const fetcher = fetcher_ && ((...args)=>{
|
||||
const [key] = bunchee_group_0.serialize(key_);
|
||||
const [, , , PRELOAD] = bunchee_group_0.SWRGlobalState.get(bunchee_group_0.cache);
|
||||
if (key.startsWith(_internal_constants.INFINITE_PREFIX)) {
|
||||
// we want the infinite fetcher to be called.
|
||||
// handling of the PRELOAD cache happens there.
|
||||
return fetcher_(...args);
|
||||
}
|
||||
const req = PRELOAD[key];
|
||||
if (bunchee_group_0.isUndefined(req)) return fetcher_(...args);
|
||||
delete PRELOAD[key];
|
||||
return req;
|
||||
});
|
||||
return useSWRNext(key_, fetcher, config);
|
||||
};
|
||||
|
||||
const BUILT_IN_MIDDLEWARE = use.concat(middleware);
|
||||
|
||||
// It's tricky to pass generic types as parameters, so we just directly override
|
||||
// the types here.
|
||||
const withArgs = (hook)=>{
|
||||
return function useSWRArgs(...args) {
|
||||
// Get the default and inherited configuration.
|
||||
const fallbackConfig = useSWRConfig();
|
||||
// Normalize arguments.
|
||||
const [key, fn, _config] = normalize(args);
|
||||
// Merge configurations.
|
||||
const config = bunchee_group_0.mergeConfigs(fallbackConfig, _config);
|
||||
// Apply middleware
|
||||
let next = hook;
|
||||
const { use } = config;
|
||||
const middleware = (use || []).concat(BUILT_IN_MIDDLEWARE);
|
||||
for(let i = middleware.length; i--;){
|
||||
next = middleware[i](next);
|
||||
}
|
||||
return next(key, fn || config.fetcher || null, config);
|
||||
};
|
||||
};
|
||||
|
||||
// Add a callback function to a list of keyed callback functions and return
|
||||
// the unsubscribe function.
|
||||
const subscribeCallback = (key, callbacks, callback)=>{
|
||||
const keyedRevalidators = callbacks[key] || (callbacks[key] = []);
|
||||
keyedRevalidators.push(callback);
|
||||
return ()=>{
|
||||
const index = keyedRevalidators.indexOf(callback);
|
||||
if (index >= 0) {
|
||||
// O(1): faster than splice
|
||||
keyedRevalidators[index] = keyedRevalidators[keyedRevalidators.length - 1];
|
||||
keyedRevalidators.pop();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Create a custom hook with a middleware
|
||||
const withMiddleware = (useSWR, middleware)=>{
|
||||
return (...args)=>{
|
||||
const [key, fn, config] = normalize(args);
|
||||
const uses = (config.use || []).concat(middleware);
|
||||
return useSWR(key, fn, {
|
||||
...config,
|
||||
use: uses
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
setupDevTools();
|
||||
|
||||
exports.IS_REACT_LEGACY = bunchee_group_0.IS_REACT_LEGACY;
|
||||
exports.IS_SERVER = bunchee_group_0.IS_SERVER;
|
||||
exports.OBJECT = bunchee_group_0.OBJECT;
|
||||
exports.SWRConfig = bunchee_group_0.SWRConfig;
|
||||
exports.SWRGlobalState = bunchee_group_0.SWRGlobalState;
|
||||
exports.UNDEFINED = bunchee_group_0.UNDEFINED;
|
||||
exports.cache = bunchee_group_0.cache;
|
||||
exports.compare = bunchee_group_0.compare;
|
||||
exports.createCacheHelper = bunchee_group_0.createCacheHelper;
|
||||
exports.defaultConfig = bunchee_group_0.defaultConfig;
|
||||
exports.defaultConfigOptions = bunchee_group_0.defaultConfigOptions;
|
||||
exports.getTimestamp = bunchee_group_0.getTimestamp;
|
||||
exports.hasRequestAnimationFrame = bunchee_group_0.hasRequestAnimationFrame;
|
||||
exports.initCache = bunchee_group_0.initCache;
|
||||
exports.internalMutate = bunchee_group_0.internalMutate;
|
||||
exports.isDocumentDefined = bunchee_group_0.isDocumentDefined;
|
||||
exports.isFunction = bunchee_group_0.isFunction;
|
||||
exports.isLegacyDeno = bunchee_group_0.isLegacyDeno;
|
||||
exports.isPromiseLike = bunchee_group_0.isPromiseLike;
|
||||
exports.isUndefined = bunchee_group_0.isUndefined;
|
||||
exports.isWindowDefined = bunchee_group_0.isWindowDefined;
|
||||
exports.mergeConfigs = bunchee_group_0.mergeConfigs;
|
||||
exports.mergeObjects = bunchee_group_0.mergeObjects;
|
||||
exports.mutate = bunchee_group_0.mutate;
|
||||
exports.noop = bunchee_group_0.noop;
|
||||
exports.preset = bunchee_group_0.preset;
|
||||
exports.rAF = bunchee_group_0.rAF;
|
||||
exports.revalidateEvents = bunchee_group_0.events;
|
||||
exports.serialize = bunchee_group_0.serialize;
|
||||
exports.slowConnection = bunchee_group_0.slowConnection;
|
||||
exports.stableHash = bunchee_group_0.stableHash;
|
||||
exports.unload = bunchee_group_0.unload;
|
||||
exports.useIsomorphicLayoutEffect = bunchee_group_0.useIsomorphicLayoutEffect;
|
||||
exports.INFINITE_PREFIX = _internal_constants.INFINITE_PREFIX;
|
||||
exports.normalize = normalize;
|
||||
exports.preload = preload;
|
||||
exports.subscribeCallback = subscribeCallback;
|
||||
exports.useSWRConfig = useSWRConfig;
|
||||
exports.withArgs = withArgs;
|
||||
exports.withMiddleware = withMiddleware;
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
import { p as isWindowDefined, e as isFunction, q as SWRConfigContext, m as mergeObjects, d as defaultConfig, s as serialize, a as SWRGlobalState, o as cache, i as isUndefined, I as IS_SERVER, t as mergeConfigs } from '../config-context-ext53wcz.mjs';
|
||||
export { j as IS_REACT_LEGACY, O as OBJECT, S as SWRConfig, U as UNDEFINED, v as compare, c as createCacheHelper, w as defaultConfigOptions, g as getTimestamp, x as hasRequestAnimationFrame, y as initCache, f as internalMutate, z as isDocumentDefined, A as isLegacyDeno, b as isPromiseLike, k as mutate, n as noop, B as preset, r as rAF, C as revalidateEvents, D as slowConnection, G as stableHash, l as unload, u as useIsomorphicLayoutEffect } from '../config-context-ext53wcz.mjs';
|
||||
import { INFINITE_PREFIX } from './constants.mjs';
|
||||
import React, { useContext, useMemo } from 'react';
|
||||
|
||||
// @ts-expect-error
|
||||
const enableDevtools = isWindowDefined && window.__SWR_DEVTOOLS_USE__;
|
||||
const use = enableDevtools ? window.__SWR_DEVTOOLS_USE__ : [];
|
||||
const setupDevTools = ()=>{
|
||||
if (enableDevtools) {
|
||||
// @ts-expect-error
|
||||
window.__SWR_DEVTOOLS_REACT__ = React;
|
||||
}
|
||||
};
|
||||
|
||||
const normalize = (args)=>{
|
||||
return isFunction(args[1]) ? [
|
||||
args[0],
|
||||
args[1],
|
||||
args[2] || {}
|
||||
] : [
|
||||
args[0],
|
||||
null,
|
||||
(args[1] === null ? args[2] : args[1]) || {}
|
||||
];
|
||||
};
|
||||
|
||||
const useSWRConfig = ()=>{
|
||||
const parentConfig = useContext(SWRConfigContext);
|
||||
const mergedConfig = useMemo(()=>mergeObjects(defaultConfig, parentConfig), [
|
||||
parentConfig
|
||||
]);
|
||||
return mergedConfig;
|
||||
};
|
||||
|
||||
const preload = (key_, fetcher)=>{
|
||||
// preload should be a no-op on the server
|
||||
if (IS_SERVER) {
|
||||
return undefined;
|
||||
}
|
||||
const [key, fnArg] = serialize(key_);
|
||||
const [, , , PRELOAD] = SWRGlobalState.get(cache);
|
||||
// Prevent preload to be called multiple times before used.
|
||||
if (PRELOAD[key]) {
|
||||
return PRELOAD[key];
|
||||
}
|
||||
const req = fetcher(fnArg);
|
||||
PRELOAD[key] = req;
|
||||
return req;
|
||||
};
|
||||
const middleware = (useSWRNext)=>(key_, fetcher_, config)=>{
|
||||
// fetcher might be a sync function, so this should not be an async function
|
||||
const fetcher = fetcher_ && ((...args)=>{
|
||||
const [key] = serialize(key_);
|
||||
const [, , , PRELOAD] = SWRGlobalState.get(cache);
|
||||
if (key.startsWith(INFINITE_PREFIX)) {
|
||||
// we want the infinite fetcher to be called.
|
||||
// handling of the PRELOAD cache happens there.
|
||||
return fetcher_(...args);
|
||||
}
|
||||
const req = PRELOAD[key];
|
||||
if (isUndefined(req)) return fetcher_(...args);
|
||||
delete PRELOAD[key];
|
||||
return req;
|
||||
});
|
||||
return useSWRNext(key_, fetcher, config);
|
||||
};
|
||||
|
||||
const BUILT_IN_MIDDLEWARE = use.concat(middleware);
|
||||
|
||||
// It's tricky to pass generic types as parameters, so we just directly override
|
||||
// the types here.
|
||||
const withArgs = (hook)=>{
|
||||
return function useSWRArgs(...args) {
|
||||
// Get the default and inherited configuration.
|
||||
const fallbackConfig = useSWRConfig();
|
||||
// Normalize arguments.
|
||||
const [key, fn, _config] = normalize(args);
|
||||
// Merge configurations.
|
||||
const config = mergeConfigs(fallbackConfig, _config);
|
||||
// Apply middleware
|
||||
let next = hook;
|
||||
const { use } = config;
|
||||
const middleware = (use || []).concat(BUILT_IN_MIDDLEWARE);
|
||||
for(let i = middleware.length; i--;){
|
||||
next = middleware[i](next);
|
||||
}
|
||||
return next(key, fn || config.fetcher || null, config);
|
||||
};
|
||||
};
|
||||
|
||||
// Add a callback function to a list of keyed callback functions and return
|
||||
// the unsubscribe function.
|
||||
const subscribeCallback = (key, callbacks, callback)=>{
|
||||
const keyedRevalidators = callbacks[key] || (callbacks[key] = []);
|
||||
keyedRevalidators.push(callback);
|
||||
return ()=>{
|
||||
const index = keyedRevalidators.indexOf(callback);
|
||||
if (index >= 0) {
|
||||
// O(1): faster than splice
|
||||
keyedRevalidators[index] = keyedRevalidators[keyedRevalidators.length - 1];
|
||||
keyedRevalidators.pop();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Create a custom hook with a middleware
|
||||
const withMiddleware = (useSWR, middleware)=>{
|
||||
return (...args)=>{
|
||||
const [key, fn, config] = normalize(args);
|
||||
const uses = (config.use || []).concat(middleware);
|
||||
return useSWR(key, fn, {
|
||||
...config,
|
||||
use: uses
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
setupDevTools();
|
||||
|
||||
export { INFINITE_PREFIX, IS_SERVER, SWRGlobalState, cache, defaultConfig, isFunction, isUndefined, isWindowDefined, mergeConfigs, mergeObjects, normalize, preload, serialize, subscribeCallback, useSWRConfig, withArgs, withMiddleware };
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
export { s as serialize } from '../serialize-bwklnvdf.js';
|
||||
export { S as SWRConfig } from '../types-l2s7dphv.js';
|
||||
export { INFINITE_PREFIX } from './constants.mjs';
|
||||
export { p as preload } from '../server-preload-cqglm8k4.js';
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
export { s as serialize } from '../serialize-f94ud86v.js';
|
||||
import { K as Key, B as BareFetcher, F as FetcherResponse, C as CacheData } from '../types-m1fld6v2.js';
|
||||
export { S as SWRConfig } from '../types-m1fld6v2.js';
|
||||
export { INFINITE_PREFIX } from './constants.js';
|
||||
|
||||
type PreloadFetcher<Data = unknown, SWRKey extends Key = Key> = SWRKey extends () => infer Arg ? (arg: Arg) => FetcherResponse<Data> : SWRKey extends infer Arg ? (arg: Arg) => FetcherResponse<Data> : never;
|
||||
declare const preload: <Data = any, SWRKey extends Key = Key, Fetcher extends BareFetcher = PreloadFetcher<Data, SWRKey>>(key_: SWRKey, fetcher: Fetcher) => CacheData<Data>;
|
||||
|
||||
export { preload };
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
var index_js = require('./index.js');
|
||||
var constants_js = require('./constants.js');
|
||||
|
||||
// Shared state between server components and client components
|
||||
const noop = ()=>{};
|
||||
// Using noop() as the undefined value as undefined can be replaced
|
||||
// by something else. Prettier ignore and extra parentheses are necessary here
|
||||
// to ensure that tsc doesn't remove the __NOINLINE__ comment.
|
||||
// prettier-ignore
|
||||
const UNDEFINED = /*#__NOINLINE__*/ noop();
|
||||
const OBJECT = Object;
|
||||
const isUndefined = (v)=>v === UNDEFINED;
|
||||
const isFunction = (v)=>typeof v == 'function';
|
||||
|
||||
// use WeakMap to store the object->key mapping
|
||||
// so the objects can be garbage collected.
|
||||
// WeakMap uses a hashtable under the hood, so the lookup
|
||||
// complexity is almost O(1).
|
||||
const table = new WeakMap();
|
||||
const getTypeName = (value)=>OBJECT.prototype.toString.call(value);
|
||||
const isObjectTypeName = (typeName, type)=>typeName === `[object ${type}]`;
|
||||
// counter of the key
|
||||
let counter = 0;
|
||||
// A stable hash implementation that supports:
|
||||
// - Fast and ensures unique hash properties
|
||||
// - Handles unserializable values
|
||||
// - Handles object key ordering
|
||||
// - Generates short results
|
||||
//
|
||||
// This is not a serialization function, and the result is not guaranteed to be
|
||||
// parsable.
|
||||
const stableHash = (arg)=>{
|
||||
const type = typeof arg;
|
||||
const typeName = getTypeName(arg);
|
||||
const isDate = isObjectTypeName(typeName, 'Date');
|
||||
const isRegex = isObjectTypeName(typeName, 'RegExp');
|
||||
const isPlainObject = isObjectTypeName(typeName, 'Object');
|
||||
let result;
|
||||
let index;
|
||||
if (OBJECT(arg) === arg && !isDate && !isRegex) {
|
||||
// Object/function, not null/date/regexp. Use WeakMap to store the id first.
|
||||
// If it's already hashed, directly return the result.
|
||||
result = table.get(arg);
|
||||
if (result) return result;
|
||||
// Store the hash first for circular reference detection before entering the
|
||||
// recursive `stableHash` calls.
|
||||
// For other objects like set and map, we use this id directly as the hash.
|
||||
result = ++counter + '~';
|
||||
table.set(arg, result);
|
||||
if (Array.isArray(arg)) {
|
||||
// Array.
|
||||
result = '@';
|
||||
for(index = 0; index < arg.length; index++){
|
||||
result += stableHash(arg[index]) + ',';
|
||||
}
|
||||
table.set(arg, result);
|
||||
}
|
||||
if (isPlainObject) {
|
||||
// Object, sort keys.
|
||||
result = '#';
|
||||
const keys = OBJECT.keys(arg).sort();
|
||||
while(!isUndefined(index = keys.pop())){
|
||||
if (!isUndefined(arg[index])) {
|
||||
result += index + ':' + stableHash(arg[index]) + ',';
|
||||
}
|
||||
}
|
||||
table.set(arg, result);
|
||||
}
|
||||
} else {
|
||||
result = isDate ? arg.toJSON() : type == 'symbol' ? arg.toString() : type == 'string' ? JSON.stringify(arg) : '' + arg;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const serialize = (key)=>{
|
||||
if (isFunction(key)) {
|
||||
try {
|
||||
key = key();
|
||||
} catch (err) {
|
||||
// dependencies not ready
|
||||
key = '';
|
||||
}
|
||||
}
|
||||
// Use the original key as the argument of fetcher. This can be a string or an
|
||||
// array of values.
|
||||
const args = key;
|
||||
// If key is not falsy, or not an empty array, hash it.
|
||||
key = typeof key == 'string' ? key : (Array.isArray(key) ? key.length : key) ? stableHash(key) : '';
|
||||
return [
|
||||
key,
|
||||
args
|
||||
];
|
||||
};
|
||||
|
||||
const preload = (key_, fetcher)=>{
|
||||
const [key, fnArg] = serialize(key_);
|
||||
return {
|
||||
[key]: fetcher(fnArg)
|
||||
};
|
||||
};
|
||||
|
||||
Object.defineProperty(exports, "SWRConfig", {
|
||||
enumerable: true,
|
||||
get: function () { return index_js.SWRConfig; }
|
||||
});
|
||||
Object.defineProperty(exports, "INFINITE_PREFIX", {
|
||||
enumerable: true,
|
||||
get: function () { return constants_js.INFINITE_PREFIX; }
|
||||
});
|
||||
exports.preload = preload;
|
||||
exports.serialize = serialize;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
export { s as serialize } from '../serialize-hqt5osie.mjs';
|
||||
export { SWRConfig } from './index.mjs';
|
||||
export { INFINITE_PREFIX } from './constants.mjs';
|
||||
export { p as preload } from '../server-preload-jludd90x.mjs';
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
export { A as Arguments, B as BareFetcher, m as BlockingData, n as Broadcaster, h as Cache, C as CacheData, l as Fetcher, F as FetcherResponse, k as FullConfiguration, G as GlobalState, I as InternalConfiguration, o as IsLoadingResponse, K as Key, p as KeyLoader, q as KeyedMutator, M as Middleware, r as Mutator, d as MutatorCallback, s as MutatorConfig, t as MutatorFn, g as MutatorOptions, v as MutatorWrapper, P as ProviderConfiguration, w as PublicConfiguration, R as RevalidateCallback, x as RevalidateEvent, y as Revalidator, z as RevalidatorOptions, b as SWRConfigValue, e as SWRConfiguration, a as SWRHook, c as SWRResponse, i as ScopedMutator, j as State, D as StateDependencies, f as StrictTupleKey, E as UnloadOptions, U as Unloader } from '../types-l2s7dphv.js';
|
||||
import '../events-ny18dyc2.js';
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
export { A as Arguments, B as BareFetcher, j as BlockingData, k as Broadcaster, a as Cache, C as CacheData, f as Fetcher, F as FetcherResponse, d as FullConfiguration, G as GlobalState, I as InternalConfiguration, l as IsLoadingResponse, K as Key, m as KeyLoader, n as KeyedMutator, i as Middleware, o as Mutator, M as MutatorCallback, p as MutatorConfig, q as MutatorFn, e as MutatorOptions, r as MutatorWrapper, P as ProviderConfiguration, s as PublicConfiguration, R as RevalidateCallback, t as RevalidateEvent, u as Revalidator, v as RevalidatorOptions, w as SWRConfigValue, g as SWRConfiguration, h as SWRHook, x as SWRResponse, b as ScopedMutator, c as State, y as StateDependencies, z as StrictTupleKey, D as UnloadOptions, U as Unloader } from '../types-m1fld6v2.js';
|
||||
import '../events-ny18dyc2.js';
|
||||
+1
@@ -0,0 +1 @@
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
|
||||
+655
@@ -0,0 +1,655 @@
|
||||
'use client';
|
||||
import React, { useEffect, useLayoutEffect, createContext, useContext, useMemo, useRef, createElement } from 'react';
|
||||
import { dequal } from 'dequal/lite';
|
||||
|
||||
const FOCUS_EVENT = 0;
|
||||
const RECONNECT_EVENT = 1;
|
||||
const MUTATE_EVENT = 2;
|
||||
const ERROR_REVALIDATE_EVENT = 3;
|
||||
const UNLOAD_EVENT = 4;
|
||||
|
||||
var events = {
|
||||
__proto__: null,
|
||||
ERROR_REVALIDATE_EVENT: ERROR_REVALIDATE_EVENT,
|
||||
FOCUS_EVENT: FOCUS_EVENT,
|
||||
MUTATE_EVENT: MUTATE_EVENT,
|
||||
RECONNECT_EVENT: RECONNECT_EVENT,
|
||||
UNLOAD_EVENT: UNLOAD_EVENT
|
||||
};
|
||||
|
||||
// Global state used to deduplicate requests and store listeners
|
||||
const SWRGlobalState = new WeakMap();
|
||||
|
||||
// Shared state between server components and client components
|
||||
const noop = ()=>{};
|
||||
// Using noop() as the undefined value as undefined can be replaced
|
||||
// by something else. Prettier ignore and extra parentheses are necessary here
|
||||
// to ensure that tsc doesn't remove the __NOINLINE__ comment.
|
||||
// prettier-ignore
|
||||
const UNDEFINED = /*#__NOINLINE__*/ noop();
|
||||
const OBJECT = Object;
|
||||
const isUndefined = (v)=>v === UNDEFINED;
|
||||
const isFunction = (v)=>typeof v == 'function';
|
||||
const mergeObjects = (a, b)=>({
|
||||
...a,
|
||||
...b
|
||||
});
|
||||
const isPromiseLike = (x)=>isFunction(x.then);
|
||||
|
||||
const EMPTY_CACHE = {};
|
||||
const INITIAL_CACHE = {};
|
||||
const STR_UNDEFINED = 'undefined';
|
||||
// NOTE: Use the function to guarantee it's re-evaluated between jsdom and node runtime for tests.
|
||||
const isWindowDefined = typeof window != STR_UNDEFINED;
|
||||
const isDocumentDefined = typeof document != STR_UNDEFINED;
|
||||
const isLegacyDeno = isWindowDefined && 'Deno' in window;
|
||||
const hasRequestAnimationFrame = ()=>isWindowDefined && typeof window['requestAnimationFrame'] != STR_UNDEFINED;
|
||||
const createCacheHelper = (cache, key)=>{
|
||||
const state = SWRGlobalState.get(cache);
|
||||
return [
|
||||
// Getter
|
||||
()=>!isUndefined(key) && cache.get(key) || EMPTY_CACHE,
|
||||
// Setter
|
||||
(info)=>{
|
||||
if (!isUndefined(key)) {
|
||||
const prev = cache.get(key);
|
||||
// Before writing to the store, we keep the value in the initial cache
|
||||
// if it's not there yet.
|
||||
if (!(key in INITIAL_CACHE)) {
|
||||
INITIAL_CACHE[key] = prev;
|
||||
}
|
||||
state[5](key, mergeObjects(prev, info), prev || EMPTY_CACHE);
|
||||
}
|
||||
},
|
||||
// Subscriber
|
||||
state[6],
|
||||
// Get server cache snapshot
|
||||
()=>{
|
||||
if (!isUndefined(key)) {
|
||||
// If the cache was updated on the client, we return the stored initial value.
|
||||
if (key in INITIAL_CACHE) return INITIAL_CACHE[key];
|
||||
}
|
||||
// If we haven't done any client-side updates, we return the current value.
|
||||
return !isUndefined(key) && cache.get(key) || EMPTY_CACHE;
|
||||
}
|
||||
];
|
||||
} // export { UNDEFINED, OBJECT, isUndefined, isFunction, mergeObjects, isPromiseLike }
|
||||
;
|
||||
|
||||
/**
|
||||
* Due to the bug https://bugs.chromium.org/p/chromium/issues/detail?id=678075,
|
||||
* it's not reliable to detect if the browser is currently online or offline
|
||||
* based on `navigator.onLine`.
|
||||
* As a workaround, we always assume it's online on the first load, and change
|
||||
* the status upon `online` or `offline` events.
|
||||
*/ let online = true;
|
||||
const isOnline = ()=>online;
|
||||
// For node and React Native, `add/removeEventListener` doesn't exist on window.
|
||||
const [onWindowEvent, offWindowEvent] = isWindowDefined && window.addEventListener ? [
|
||||
window.addEventListener.bind(window),
|
||||
window.removeEventListener.bind(window)
|
||||
] : [
|
||||
noop,
|
||||
noop
|
||||
];
|
||||
const isVisible = ()=>{
|
||||
const visibilityState = isDocumentDefined && document.visibilityState;
|
||||
return isUndefined(visibilityState) || visibilityState !== 'hidden';
|
||||
};
|
||||
const initFocus = (callback)=>{
|
||||
// focus revalidate
|
||||
if (isDocumentDefined) {
|
||||
document.addEventListener('visibilitychange', callback);
|
||||
}
|
||||
onWindowEvent('focus', callback);
|
||||
return ()=>{
|
||||
if (isDocumentDefined) {
|
||||
document.removeEventListener('visibilitychange', callback);
|
||||
}
|
||||
offWindowEvent('focus', callback);
|
||||
};
|
||||
};
|
||||
const initReconnect = (callback)=>{
|
||||
// revalidate on reconnected
|
||||
const onOnline = ()=>{
|
||||
online = true;
|
||||
callback();
|
||||
};
|
||||
// nothing to revalidate, just update the status
|
||||
const onOffline = ()=>{
|
||||
online = false;
|
||||
};
|
||||
onWindowEvent('online', onOnline);
|
||||
onWindowEvent('offline', onOffline);
|
||||
return ()=>{
|
||||
offWindowEvent('online', onOnline);
|
||||
offWindowEvent('offline', onOffline);
|
||||
};
|
||||
};
|
||||
const preset = {
|
||||
isOnline,
|
||||
isVisible
|
||||
};
|
||||
const defaultConfigOptions = {
|
||||
initFocus,
|
||||
initReconnect
|
||||
};
|
||||
|
||||
const IS_REACT_LEGACY = !React.useId;
|
||||
const IS_SERVER = !isWindowDefined || isLegacyDeno;
|
||||
// Polyfill requestAnimationFrame
|
||||
const rAF = (f)=>hasRequestAnimationFrame() ? window['requestAnimationFrame'](f) : setTimeout(f, 1);
|
||||
// React currently throws a warning when using useLayoutEffect on the server.
|
||||
// To get around it, we can conditionally useEffect on the server (no-op) and
|
||||
// useLayoutEffect in the browser.
|
||||
const useIsomorphicLayoutEffect = IS_SERVER ? useEffect : useLayoutEffect;
|
||||
// This assignment is to extend the Navigator type to use effectiveType.
|
||||
const navigatorConnection = typeof navigator !== 'undefined' && navigator.connection;
|
||||
// Adjust the config based on slow connection status (<= 70Kbps).
|
||||
const slowConnection = !IS_SERVER && navigatorConnection && ([
|
||||
'slow-2g',
|
||||
'2g'
|
||||
].includes(navigatorConnection.effectiveType) || navigatorConnection.saveData);
|
||||
|
||||
// use WeakMap to store the object->key mapping
|
||||
// so the objects can be garbage collected.
|
||||
// WeakMap uses a hashtable under the hood, so the lookup
|
||||
// complexity is almost O(1).
|
||||
const table = new WeakMap();
|
||||
const getTypeName = (value)=>OBJECT.prototype.toString.call(value);
|
||||
const isObjectTypeName = (typeName, type)=>typeName === `[object ${type}]`;
|
||||
// counter of the key
|
||||
let counter = 0;
|
||||
// A stable hash implementation that supports:
|
||||
// - Fast and ensures unique hash properties
|
||||
// - Handles unserializable values
|
||||
// - Handles object key ordering
|
||||
// - Generates short results
|
||||
//
|
||||
// This is not a serialization function, and the result is not guaranteed to be
|
||||
// parsable.
|
||||
const stableHash = (arg)=>{
|
||||
const type = typeof arg;
|
||||
const typeName = getTypeName(arg);
|
||||
const isDate = isObjectTypeName(typeName, 'Date');
|
||||
const isRegex = isObjectTypeName(typeName, 'RegExp');
|
||||
const isPlainObject = isObjectTypeName(typeName, 'Object');
|
||||
let result;
|
||||
let index;
|
||||
if (OBJECT(arg) === arg && !isDate && !isRegex) {
|
||||
// Object/function, not null/date/regexp. Use WeakMap to store the id first.
|
||||
// If it's already hashed, directly return the result.
|
||||
result = table.get(arg);
|
||||
if (result) return result;
|
||||
// Store the hash first for circular reference detection before entering the
|
||||
// recursive `stableHash` calls.
|
||||
// For other objects like set and map, we use this id directly as the hash.
|
||||
result = ++counter + '~';
|
||||
table.set(arg, result);
|
||||
if (Array.isArray(arg)) {
|
||||
// Array.
|
||||
result = '@';
|
||||
for(index = 0; index < arg.length; index++){
|
||||
result += stableHash(arg[index]) + ',';
|
||||
}
|
||||
table.set(arg, result);
|
||||
}
|
||||
if (isPlainObject) {
|
||||
// Object, sort keys.
|
||||
result = '#';
|
||||
const keys = OBJECT.keys(arg).sort();
|
||||
while(!isUndefined(index = keys.pop())){
|
||||
if (!isUndefined(arg[index])) {
|
||||
result += index + ':' + stableHash(arg[index]) + ',';
|
||||
}
|
||||
}
|
||||
table.set(arg, result);
|
||||
}
|
||||
} else {
|
||||
result = isDate ? arg.toJSON() : type == 'symbol' ? arg.toString() : type == 'string' ? JSON.stringify(arg) : '' + arg;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const serialize = (key)=>{
|
||||
if (isFunction(key)) {
|
||||
try {
|
||||
key = key();
|
||||
} catch (err) {
|
||||
// dependencies not ready
|
||||
key = '';
|
||||
}
|
||||
}
|
||||
// Use the original key as the argument of fetcher. This can be a string or an
|
||||
// array of values.
|
||||
const args = key;
|
||||
// If key is not falsy, or not an empty array, hash it.
|
||||
key = typeof key == 'string' ? key : (Array.isArray(key) ? key.length : key) ? stableHash(key) : '';
|
||||
return [
|
||||
key,
|
||||
args
|
||||
];
|
||||
};
|
||||
|
||||
// Global timestamp.
|
||||
let __timestamp = 0;
|
||||
const getTimestamp = ()=>++__timestamp;
|
||||
|
||||
async function internalMutate(...args) {
|
||||
const [cache, _key, _data, _opts] = args;
|
||||
// When passing as a boolean, it's explicitly used to disable/enable
|
||||
// revalidation.
|
||||
const options = mergeObjects({
|
||||
populateCache: true,
|
||||
throwOnError: true
|
||||
}, typeof _opts === 'boolean' ? {
|
||||
revalidate: _opts
|
||||
} : _opts || {});
|
||||
let populateCache = options.populateCache;
|
||||
const rollbackOnErrorOption = options.rollbackOnError;
|
||||
let optimisticData = options.optimisticData;
|
||||
const rollbackOnError = (error)=>{
|
||||
return typeof rollbackOnErrorOption === 'function' ? rollbackOnErrorOption(error) : rollbackOnErrorOption !== false;
|
||||
};
|
||||
const throwOnError = options.throwOnError;
|
||||
// If the second argument is a key filter, return the mutation results for all
|
||||
// filtered keys.
|
||||
if (isFunction(_key)) {
|
||||
const keyFilter = _key;
|
||||
const matchedKeys = [];
|
||||
const it = cache.keys();
|
||||
for (const key of it){
|
||||
if (// Skip the special useSWRInfinite and useSWRSubscription keys.
|
||||
!/^\$(inf|sub)\$/.test(key) && keyFilter(cache.get(key)._k)) {
|
||||
matchedKeys.push(key);
|
||||
}
|
||||
}
|
||||
return Promise.all(matchedKeys.map(mutateByKey));
|
||||
}
|
||||
return mutateByKey(_key);
|
||||
async function mutateByKey(_k) {
|
||||
// Serialize key
|
||||
const [key] = serialize(_k);
|
||||
if (!key) return;
|
||||
const [get, set] = createCacheHelper(cache, key);
|
||||
const [EVENT_REVALIDATORS, MUTATION, FETCH, PRELOAD] = SWRGlobalState.get(cache);
|
||||
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.
|
||||
delete FETCH[key];
|
||||
delete PRELOAD[key];
|
||||
if (revalidators && revalidators[0]) {
|
||||
return revalidators[0](MUTATE_EVENT).then(()=>get().data);
|
||||
}
|
||||
}
|
||||
return get().data;
|
||||
};
|
||||
// If there is no new data provided, revalidate the key with current state.
|
||||
if (args.length < 3) {
|
||||
// Revalidate and broadcast state.
|
||||
return startRevalidate();
|
||||
}
|
||||
let data = _data;
|
||||
let error;
|
||||
let isError = false;
|
||||
// Update global timestamps.
|
||||
const beforeMutationTs = getTimestamp();
|
||||
MUTATION[key] = [
|
||||
beforeMutationTs,
|
||||
0
|
||||
];
|
||||
const hasOptimisticData = !isUndefined(optimisticData);
|
||||
const state = get();
|
||||
// `displayedData` is the current value on screen. It could be the optimistic value
|
||||
// that is going to be overridden by a `committedData`, or get reverted back.
|
||||
// `committedData` is the validated value that comes from a fetch or mutation.
|
||||
const displayedData = state.data;
|
||||
const currentData = state._c;
|
||||
const committedData = isUndefined(currentData) ? displayedData : currentData;
|
||||
// Do optimistic data update.
|
||||
if (hasOptimisticData) {
|
||||
optimisticData = isFunction(optimisticData) ? optimisticData(committedData, displayedData) : optimisticData;
|
||||
// When we set optimistic data, backup the current committedData data in `_c`.
|
||||
set({
|
||||
data: optimisticData,
|
||||
_c: committedData
|
||||
});
|
||||
}
|
||||
if (isFunction(data)) {
|
||||
// `data` is a function, call it passing current cache value.
|
||||
try {
|
||||
data = data(committedData);
|
||||
} catch (err) {
|
||||
// If it throws an error synchronously, we shouldn't update the cache.
|
||||
error = err;
|
||||
isError = true;
|
||||
}
|
||||
}
|
||||
// `data` is a promise/thenable, resolve the final data first.
|
||||
if (data && isPromiseLike(data)) {
|
||||
// This means that the mutation is async, we need to check timestamps to
|
||||
// avoid race conditions.
|
||||
data = await data.catch((err)=>{
|
||||
error = err;
|
||||
isError = true;
|
||||
});
|
||||
// Check if other mutations have occurred since we've started this mutation.
|
||||
// If there's a race we don't update cache or broadcast the change,
|
||||
// just return the data.
|
||||
if (beforeMutationTs !== MUTATION[key][0]) {
|
||||
if (isError) throw error;
|
||||
return data;
|
||||
} else if (isError && hasOptimisticData && rollbackOnError(error)) {
|
||||
// Rollback. Always populate the cache in this case but without
|
||||
// transforming the data.
|
||||
populateCache = true;
|
||||
// Reset data to be the latest committed data, and clear the `_c` value.
|
||||
set({
|
||||
data: committedData,
|
||||
_c: UNDEFINED
|
||||
});
|
||||
}
|
||||
}
|
||||
// If we should write back the cache after request.
|
||||
if (populateCache) {
|
||||
if (!isError) {
|
||||
// Transform the result into data.
|
||||
if (isFunction(populateCache)) {
|
||||
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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// Reset the timestamp to mark the mutation has ended.
|
||||
MUTATION[key][1] = getTimestamp();
|
||||
// Update existing SWR Hooks' internal states:
|
||||
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 (isError) {
|
||||
if (throwOnError) throw error;
|
||||
return;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
const revalidateAllKeys = (revalidators, type)=>{
|
||||
for(const key in revalidators){
|
||||
if (revalidators[key][0]) revalidators[key][0](type);
|
||||
}
|
||||
};
|
||||
const initCache = (provider, options)=>{
|
||||
// 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
|
||||
// the cache.
|
||||
// The provider's global state might be already initialized. Let's try to get the
|
||||
// global state associated with the provider first.
|
||||
if (!SWRGlobalState.has(provider)) {
|
||||
const opts = mergeObjects(defaultConfigOptions, options);
|
||||
// If there's no global state bound to the provider, create a new one with the
|
||||
// new mutate function.
|
||||
const EVENT_REVALIDATORS = Object.create(null);
|
||||
const mutate = internalMutate.bind(UNDEFINED, provider);
|
||||
let unmount = noop;
|
||||
const subscriptions = Object.create(null);
|
||||
const subscribe = (key, callback)=>{
|
||||
const subs = subscriptions[key] || [];
|
||||
subscriptions[key] = subs;
|
||||
subs.push(callback);
|
||||
return ()=>{
|
||||
const index = subs.indexOf(callback);
|
||||
if (index >= 0) {
|
||||
// O(1): swap with the last element and pop
|
||||
subs[index] = subs[subs.length - 1];
|
||||
subs.pop();
|
||||
}
|
||||
};
|
||||
};
|
||||
const setter = (key, value, prev)=>{
|
||||
provider.set(key, value);
|
||||
const subs = subscriptions[key];
|
||||
if (subs) {
|
||||
for (const fn of subs){
|
||||
fn(value, prev);
|
||||
}
|
||||
}
|
||||
};
|
||||
const unload = (unloadOptions)=>{
|
||||
const state = SWRGlobalState.get(provider);
|
||||
const [, MUTATION, FETCH, PRELOAD] = state;
|
||||
const ts = getTimestamp();
|
||||
// Bump the unload generation so in-flight writes that bypass the
|
||||
// request markers (e.g. `useSWRInfinite` page responses) know to
|
||||
// discard themselves when they resolve.
|
||||
state[8]++;
|
||||
// Invalidate all in-flight requests and mutations first, so nothing
|
||||
// that resolves after this point can write back to the cache: fetches
|
||||
// fail the concurrent request marker check, and mutations fail the
|
||||
// timestamp race check. Mutation timestamps are overwritten instead of
|
||||
// deleted because ongoing mutations read them on resolution.
|
||||
for(const key in FETCH)delete FETCH[key];
|
||||
for(const key in PRELOAD)delete PRELOAD[key];
|
||||
for(const key in MUTATION)MUTATION[key] = [
|
||||
ts,
|
||||
ts
|
||||
];
|
||||
// Delete all entries — including the special `useSWRInfinite` and
|
||||
// `useSWRSubscription` keys — and notify the subscribers of each key
|
||||
// so mounted hooks re-render with the empty cache. Subscribers receive
|
||||
// the same empty state object that cache reads resolve to for missing
|
||||
// entries.
|
||||
const emptyState = {};
|
||||
for (const key of [
|
||||
...provider.keys()
|
||||
]){
|
||||
const prev = provider.get(key);
|
||||
provider.delete(key);
|
||||
const subs = subscriptions[key];
|
||||
if (subs) {
|
||||
for (const fn of subs){
|
||||
fn(emptyState, prev);
|
||||
}
|
||||
}
|
||||
}
|
||||
// The unload event runs last so the fresh requests it may start are
|
||||
// not invalidated by the cleanup above. It always fires — even without
|
||||
// revalidation — and to every hook on the key, so each one can drop
|
||||
// the previous data kept by `keepPreviousData`. Only the first hook
|
||||
// revalidates, consistent with how mutations revalidate a key.
|
||||
const revalidate = !unloadOptions || unloadOptions.revalidate !== false;
|
||||
for(const key in EVENT_REVALIDATORS){
|
||||
const revalidators = EVENT_REVALIDATORS[key];
|
||||
for(let i = 0; i < revalidators.length; i++){
|
||||
revalidators[i](UNLOAD_EVENT, {
|
||||
revalidate: revalidate && !i
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
const initProvider = ()=>{
|
||||
if (!SWRGlobalState.has(provider)) {
|
||||
// Update the state if it's new, or if the provider has been extended.
|
||||
SWRGlobalState.set(provider, [
|
||||
EVENT_REVALIDATORS,
|
||||
Object.create(null),
|
||||
Object.create(null),
|
||||
Object.create(null),
|
||||
mutate,
|
||||
setter,
|
||||
subscribe,
|
||||
unload,
|
||||
0
|
||||
]);
|
||||
if (!IS_SERVER) {
|
||||
// When listening to the native events for auto revalidations,
|
||||
// we intentionally put a delay (setTimeout) here to make sure they are
|
||||
// fired after immediate JavaScript executions, which can be
|
||||
// React's state updates.
|
||||
// This avoids some unnecessary revalidations such as
|
||||
// https://github.com/vercel/swr/issues/1680.
|
||||
const releaseFocus = opts.initFocus(setTimeout.bind(UNDEFINED, revalidateAllKeys.bind(UNDEFINED, EVENT_REVALIDATORS, FOCUS_EVENT)));
|
||||
const releaseReconnect = opts.initReconnect(setTimeout.bind(UNDEFINED, revalidateAllKeys.bind(UNDEFINED, EVENT_REVALIDATORS, RECONNECT_EVENT)));
|
||||
unmount = ()=>{
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
releaseFocus && releaseFocus();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
releaseReconnect && releaseReconnect();
|
||||
// When un-mounting, we need to remove the cache provider from the state
|
||||
// storage too because it's a side-effect. Otherwise, when re-mounting we
|
||||
// will not re-register those event listeners.
|
||||
SWRGlobalState.delete(provider);
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
initProvider();
|
||||
// This is a new provider, we need to initialize it and setup DOM events
|
||||
// listeners for `focus` and `reconnect` actions.
|
||||
// We might want to inject an extra layer on top of `provider` in the future,
|
||||
// such as key serialization, auto GC, etc.
|
||||
// For now, it's just a `Map` interface without any modifications.
|
||||
return [
|
||||
provider,
|
||||
mutate,
|
||||
initProvider,
|
||||
unmount,
|
||||
unload
|
||||
];
|
||||
}
|
||||
const state = SWRGlobalState.get(provider);
|
||||
return [
|
||||
provider,
|
||||
state[4],
|
||||
UNDEFINED,
|
||||
UNDEFINED,
|
||||
state[7]
|
||||
];
|
||||
};
|
||||
|
||||
// error retry
|
||||
const onErrorRetry = (_, __, config, revalidate, opts)=>{
|
||||
const maxRetryCount = config.errorRetryCount;
|
||||
const currentRetryCount = opts.retryCount;
|
||||
// Exponential backoff
|
||||
const timeout = ~~((Math.random() + 0.5) * (1 << (currentRetryCount < 8 ? currentRetryCount : 8))) * config.errorRetryInterval;
|
||||
if (!isUndefined(maxRetryCount) && currentRetryCount > maxRetryCount) {
|
||||
return;
|
||||
}
|
||||
setTimeout(revalidate, timeout, opts);
|
||||
};
|
||||
const compare = dequal;
|
||||
// Default cache provider
|
||||
const [cache, mutate, , , unload] = initCache(new Map());
|
||||
// Default config
|
||||
const defaultConfig = mergeObjects({
|
||||
// events
|
||||
onLoadingSlow: noop,
|
||||
onSuccess: noop,
|
||||
onError: noop,
|
||||
onErrorRetry,
|
||||
onDiscarded: noop,
|
||||
// switches
|
||||
revalidateOnFocus: true,
|
||||
revalidateOnReconnect: true,
|
||||
revalidateIfStale: true,
|
||||
shouldRetryOnError: true,
|
||||
// timeouts
|
||||
errorRetryInterval: slowConnection ? 10000 : 5000,
|
||||
focusThrottleInterval: 5 * 1000,
|
||||
dedupingInterval: 2 * 1000,
|
||||
loadingTimeout: slowConnection ? 5000 : 3000,
|
||||
// providers
|
||||
compare,
|
||||
isPaused: ()=>false,
|
||||
cache,
|
||||
mutate,
|
||||
unload,
|
||||
fallback: {}
|
||||
}, // use web preset by default
|
||||
preset);
|
||||
|
||||
const mergeConfigs = (a, b)=>{
|
||||
// Need to create a new object to avoid mutating the original here.
|
||||
const v = mergeObjects(a, b);
|
||||
// If two configs are provided, merge their `use`, `fallback`, and
|
||||
// `cacheData` options.
|
||||
if (b) {
|
||||
const { use: u1, fallback: f1, cacheData: c1 } = a;
|
||||
const { use: u2, fallback: f2, cacheData: c2 } = b;
|
||||
if (u1 && u2) {
|
||||
v.use = u1.concat(u2);
|
||||
}
|
||||
if (f1 && f2) {
|
||||
v.fallback = mergeObjects(f1, f2);
|
||||
}
|
||||
if (c1 && c2) {
|
||||
v.cacheData = mergeObjects(c1, c2);
|
||||
}
|
||||
}
|
||||
return v;
|
||||
};
|
||||
|
||||
const SWRConfigContext = createContext({});
|
||||
const SWRConfig = (props)=>{
|
||||
const { value } = props;
|
||||
const parentConfig = useContext(SWRConfigContext);
|
||||
const isFunctionalConfig = isFunction(value);
|
||||
const config = useMemo(()=>isFunctionalConfig ? value(parentConfig) : value, [
|
||||
isFunctionalConfig,
|
||||
parentConfig,
|
||||
value
|
||||
]);
|
||||
// Extend parent context values and middleware.
|
||||
const extendedConfig = useMemo(()=>isFunctionalConfig ? config : mergeConfigs(parentConfig, config), [
|
||||
isFunctionalConfig,
|
||||
parentConfig,
|
||||
config
|
||||
]);
|
||||
// Should not use the inherited provider.
|
||||
const provider = config && config.provider;
|
||||
// initialize the cache only on first access.
|
||||
const cacheContextRef = useRef(UNDEFINED);
|
||||
if (provider && !cacheContextRef.current) {
|
||||
cacheContextRef.current = initCache(provider(extendedConfig.cache || cache), config);
|
||||
}
|
||||
const cacheContext = cacheContextRef.current;
|
||||
// Override the cache if a new provider is given.
|
||||
if (cacheContext) {
|
||||
extendedConfig.cache = cacheContext[0];
|
||||
extendedConfig.mutate = cacheContext[1];
|
||||
extendedConfig.unload = cacheContext[4];
|
||||
}
|
||||
// Unsubscribe events.
|
||||
useIsomorphicLayoutEffect(()=>{
|
||||
if (cacheContext) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
cacheContext[2] && cacheContext[2]();
|
||||
return cacheContext[3];
|
||||
}
|
||||
}, []);
|
||||
return createElement(SWRConfigContext.Provider, mergeObjects(props, {
|
||||
value: extendedConfig
|
||||
}));
|
||||
};
|
||||
|
||||
export { isLegacyDeno as A, preset as B, events as C, slowConnection as D, ERROR_REVALIDATE_EVENT as E, FOCUS_EVENT as F, stableHash as G, IS_SERVER as I, MUTATE_EVENT as M, OBJECT as O, RECONNECT_EVENT as R, SWRConfig as S, UNDEFINED as U, SWRGlobalState as a, isPromiseLike as b, createCacheHelper as c, defaultConfig as d, isFunction as e, internalMutate as f, getTimestamp as g, UNLOAD_EVENT as h, isUndefined as i, IS_REACT_LEGACY as j, mutate as k, unload as l, mergeObjects as m, noop as n, cache as o, isWindowDefined as p, SWRConfigContext as q, rAF as r, serialize as s, mergeConfigs as t, useIsomorphicLayoutEffect as u, compare as v, defaultConfigOptions as w, hasRequestAnimationFrame as x, initCache as y, isDocumentDefined as z };
|
||||
+697
@@ -0,0 +1,697 @@
|
||||
'use client';
|
||||
var React = require('react');
|
||||
var lite = require('dequal/lite');
|
||||
|
||||
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
||||
|
||||
var React__default = /*#__PURE__*/_interopDefault(React);
|
||||
|
||||
const FOCUS_EVENT = 0;
|
||||
const RECONNECT_EVENT = 1;
|
||||
const MUTATE_EVENT = 2;
|
||||
const ERROR_REVALIDATE_EVENT = 3;
|
||||
const UNLOAD_EVENT = 4;
|
||||
|
||||
var events = {
|
||||
__proto__: null,
|
||||
ERROR_REVALIDATE_EVENT: ERROR_REVALIDATE_EVENT,
|
||||
FOCUS_EVENT: FOCUS_EVENT,
|
||||
MUTATE_EVENT: MUTATE_EVENT,
|
||||
RECONNECT_EVENT: RECONNECT_EVENT,
|
||||
UNLOAD_EVENT: UNLOAD_EVENT
|
||||
};
|
||||
|
||||
// Global state used to deduplicate requests and store listeners
|
||||
const SWRGlobalState = new WeakMap();
|
||||
|
||||
// Shared state between server components and client components
|
||||
const noop = ()=>{};
|
||||
// Using noop() as the undefined value as undefined can be replaced
|
||||
// by something else. Prettier ignore and extra parentheses are necessary here
|
||||
// to ensure that tsc doesn't remove the __NOINLINE__ comment.
|
||||
// prettier-ignore
|
||||
const UNDEFINED = /*#__NOINLINE__*/ noop();
|
||||
const OBJECT = Object;
|
||||
const isUndefined = (v)=>v === UNDEFINED;
|
||||
const isFunction = (v)=>typeof v == 'function';
|
||||
const mergeObjects = (a, b)=>({
|
||||
...a,
|
||||
...b
|
||||
});
|
||||
const isPromiseLike = (x)=>isFunction(x.then);
|
||||
|
||||
const EMPTY_CACHE = {};
|
||||
const INITIAL_CACHE = {};
|
||||
const STR_UNDEFINED = 'undefined';
|
||||
// NOTE: Use the function to guarantee it's re-evaluated between jsdom and node runtime for tests.
|
||||
const isWindowDefined = typeof window != STR_UNDEFINED;
|
||||
const isDocumentDefined = typeof document != STR_UNDEFINED;
|
||||
const isLegacyDeno = isWindowDefined && 'Deno' in window;
|
||||
const hasRequestAnimationFrame = ()=>isWindowDefined && typeof window['requestAnimationFrame'] != STR_UNDEFINED;
|
||||
const createCacheHelper = (cache, key)=>{
|
||||
const state = SWRGlobalState.get(cache);
|
||||
return [
|
||||
// Getter
|
||||
()=>!isUndefined(key) && cache.get(key) || EMPTY_CACHE,
|
||||
// Setter
|
||||
(info)=>{
|
||||
if (!isUndefined(key)) {
|
||||
const prev = cache.get(key);
|
||||
// Before writing to the store, we keep the value in the initial cache
|
||||
// if it's not there yet.
|
||||
if (!(key in INITIAL_CACHE)) {
|
||||
INITIAL_CACHE[key] = prev;
|
||||
}
|
||||
state[5](key, mergeObjects(prev, info), prev || EMPTY_CACHE);
|
||||
}
|
||||
},
|
||||
// Subscriber
|
||||
state[6],
|
||||
// Get server cache snapshot
|
||||
()=>{
|
||||
if (!isUndefined(key)) {
|
||||
// If the cache was updated on the client, we return the stored initial value.
|
||||
if (key in INITIAL_CACHE) return INITIAL_CACHE[key];
|
||||
}
|
||||
// If we haven't done any client-side updates, we return the current value.
|
||||
return !isUndefined(key) && cache.get(key) || EMPTY_CACHE;
|
||||
}
|
||||
];
|
||||
} // export { UNDEFINED, OBJECT, isUndefined, isFunction, mergeObjects, isPromiseLike }
|
||||
;
|
||||
|
||||
/**
|
||||
* Due to the bug https://bugs.chromium.org/p/chromium/issues/detail?id=678075,
|
||||
* it's not reliable to detect if the browser is currently online or offline
|
||||
* based on `navigator.onLine`.
|
||||
* As a workaround, we always assume it's online on the first load, and change
|
||||
* the status upon `online` or `offline` events.
|
||||
*/ let online = true;
|
||||
const isOnline = ()=>online;
|
||||
// For node and React Native, `add/removeEventListener` doesn't exist on window.
|
||||
const [onWindowEvent, offWindowEvent] = isWindowDefined && window.addEventListener ? [
|
||||
window.addEventListener.bind(window),
|
||||
window.removeEventListener.bind(window)
|
||||
] : [
|
||||
noop,
|
||||
noop
|
||||
];
|
||||
const isVisible = ()=>{
|
||||
const visibilityState = isDocumentDefined && document.visibilityState;
|
||||
return isUndefined(visibilityState) || visibilityState !== 'hidden';
|
||||
};
|
||||
const initFocus = (callback)=>{
|
||||
// focus revalidate
|
||||
if (isDocumentDefined) {
|
||||
document.addEventListener('visibilitychange', callback);
|
||||
}
|
||||
onWindowEvent('focus', callback);
|
||||
return ()=>{
|
||||
if (isDocumentDefined) {
|
||||
document.removeEventListener('visibilitychange', callback);
|
||||
}
|
||||
offWindowEvent('focus', callback);
|
||||
};
|
||||
};
|
||||
const initReconnect = (callback)=>{
|
||||
// revalidate on reconnected
|
||||
const onOnline = ()=>{
|
||||
online = true;
|
||||
callback();
|
||||
};
|
||||
// nothing to revalidate, just update the status
|
||||
const onOffline = ()=>{
|
||||
online = false;
|
||||
};
|
||||
onWindowEvent('online', onOnline);
|
||||
onWindowEvent('offline', onOffline);
|
||||
return ()=>{
|
||||
offWindowEvent('online', onOnline);
|
||||
offWindowEvent('offline', onOffline);
|
||||
};
|
||||
};
|
||||
const preset = {
|
||||
isOnline,
|
||||
isVisible
|
||||
};
|
||||
const defaultConfigOptions = {
|
||||
initFocus,
|
||||
initReconnect
|
||||
};
|
||||
|
||||
const IS_REACT_LEGACY = !React__default.default.useId;
|
||||
const IS_SERVER = !isWindowDefined || isLegacyDeno;
|
||||
// Polyfill requestAnimationFrame
|
||||
const rAF = (f)=>hasRequestAnimationFrame() ? window['requestAnimationFrame'](f) : setTimeout(f, 1);
|
||||
// React currently throws a warning when using useLayoutEffect on the server.
|
||||
// To get around it, we can conditionally useEffect on the server (no-op) and
|
||||
// useLayoutEffect in the browser.
|
||||
const useIsomorphicLayoutEffect = IS_SERVER ? React.useEffect : React.useLayoutEffect;
|
||||
// This assignment is to extend the Navigator type to use effectiveType.
|
||||
const navigatorConnection = typeof navigator !== 'undefined' && navigator.connection;
|
||||
// Adjust the config based on slow connection status (<= 70Kbps).
|
||||
const slowConnection = !IS_SERVER && navigatorConnection && ([
|
||||
'slow-2g',
|
||||
'2g'
|
||||
].includes(navigatorConnection.effectiveType) || navigatorConnection.saveData);
|
||||
|
||||
// use WeakMap to store the object->key mapping
|
||||
// so the objects can be garbage collected.
|
||||
// WeakMap uses a hashtable under the hood, so the lookup
|
||||
// complexity is almost O(1).
|
||||
const table = new WeakMap();
|
||||
const getTypeName = (value)=>OBJECT.prototype.toString.call(value);
|
||||
const isObjectTypeName = (typeName, type)=>typeName === `[object ${type}]`;
|
||||
// counter of the key
|
||||
let counter = 0;
|
||||
// A stable hash implementation that supports:
|
||||
// - Fast and ensures unique hash properties
|
||||
// - Handles unserializable values
|
||||
// - Handles object key ordering
|
||||
// - Generates short results
|
||||
//
|
||||
// This is not a serialization function, and the result is not guaranteed to be
|
||||
// parsable.
|
||||
const stableHash = (arg)=>{
|
||||
const type = typeof arg;
|
||||
const typeName = getTypeName(arg);
|
||||
const isDate = isObjectTypeName(typeName, 'Date');
|
||||
const isRegex = isObjectTypeName(typeName, 'RegExp');
|
||||
const isPlainObject = isObjectTypeName(typeName, 'Object');
|
||||
let result;
|
||||
let index;
|
||||
if (OBJECT(arg) === arg && !isDate && !isRegex) {
|
||||
// Object/function, not null/date/regexp. Use WeakMap to store the id first.
|
||||
// If it's already hashed, directly return the result.
|
||||
result = table.get(arg);
|
||||
if (result) return result;
|
||||
// Store the hash first for circular reference detection before entering the
|
||||
// recursive `stableHash` calls.
|
||||
// For other objects like set and map, we use this id directly as the hash.
|
||||
result = ++counter + '~';
|
||||
table.set(arg, result);
|
||||
if (Array.isArray(arg)) {
|
||||
// Array.
|
||||
result = '@';
|
||||
for(index = 0; index < arg.length; index++){
|
||||
result += stableHash(arg[index]) + ',';
|
||||
}
|
||||
table.set(arg, result);
|
||||
}
|
||||
if (isPlainObject) {
|
||||
// Object, sort keys.
|
||||
result = '#';
|
||||
const keys = OBJECT.keys(arg).sort();
|
||||
while(!isUndefined(index = keys.pop())){
|
||||
if (!isUndefined(arg[index])) {
|
||||
result += index + ':' + stableHash(arg[index]) + ',';
|
||||
}
|
||||
}
|
||||
table.set(arg, result);
|
||||
}
|
||||
} else {
|
||||
result = isDate ? arg.toJSON() : type == 'symbol' ? arg.toString() : type == 'string' ? JSON.stringify(arg) : '' + arg;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const serialize = (key)=>{
|
||||
if (isFunction(key)) {
|
||||
try {
|
||||
key = key();
|
||||
} catch (err) {
|
||||
// dependencies not ready
|
||||
key = '';
|
||||
}
|
||||
}
|
||||
// Use the original key as the argument of fetcher. This can be a string or an
|
||||
// array of values.
|
||||
const args = key;
|
||||
// If key is not falsy, or not an empty array, hash it.
|
||||
key = typeof key == 'string' ? key : (Array.isArray(key) ? key.length : key) ? stableHash(key) : '';
|
||||
return [
|
||||
key,
|
||||
args
|
||||
];
|
||||
};
|
||||
|
||||
// Global timestamp.
|
||||
let __timestamp = 0;
|
||||
const getTimestamp = ()=>++__timestamp;
|
||||
|
||||
async function internalMutate(...args) {
|
||||
const [cache, _key, _data, _opts] = args;
|
||||
// When passing as a boolean, it's explicitly used to disable/enable
|
||||
// revalidation.
|
||||
const options = mergeObjects({
|
||||
populateCache: true,
|
||||
throwOnError: true
|
||||
}, typeof _opts === 'boolean' ? {
|
||||
revalidate: _opts
|
||||
} : _opts || {});
|
||||
let populateCache = options.populateCache;
|
||||
const rollbackOnErrorOption = options.rollbackOnError;
|
||||
let optimisticData = options.optimisticData;
|
||||
const rollbackOnError = (error)=>{
|
||||
return typeof rollbackOnErrorOption === 'function' ? rollbackOnErrorOption(error) : rollbackOnErrorOption !== false;
|
||||
};
|
||||
const throwOnError = options.throwOnError;
|
||||
// If the second argument is a key filter, return the mutation results for all
|
||||
// filtered keys.
|
||||
if (isFunction(_key)) {
|
||||
const keyFilter = _key;
|
||||
const matchedKeys = [];
|
||||
const it = cache.keys();
|
||||
for (const key of it){
|
||||
if (// Skip the special useSWRInfinite and useSWRSubscription keys.
|
||||
!/^\$(inf|sub)\$/.test(key) && keyFilter(cache.get(key)._k)) {
|
||||
matchedKeys.push(key);
|
||||
}
|
||||
}
|
||||
return Promise.all(matchedKeys.map(mutateByKey));
|
||||
}
|
||||
return mutateByKey(_key);
|
||||
async function mutateByKey(_k) {
|
||||
// Serialize key
|
||||
const [key] = serialize(_k);
|
||||
if (!key) return;
|
||||
const [get, set] = createCacheHelper(cache, key);
|
||||
const [EVENT_REVALIDATORS, MUTATION, FETCH, PRELOAD] = SWRGlobalState.get(cache);
|
||||
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.
|
||||
delete FETCH[key];
|
||||
delete PRELOAD[key];
|
||||
if (revalidators && revalidators[0]) {
|
||||
return revalidators[0](MUTATE_EVENT).then(()=>get().data);
|
||||
}
|
||||
}
|
||||
return get().data;
|
||||
};
|
||||
// If there is no new data provided, revalidate the key with current state.
|
||||
if (args.length < 3) {
|
||||
// Revalidate and broadcast state.
|
||||
return startRevalidate();
|
||||
}
|
||||
let data = _data;
|
||||
let error;
|
||||
let isError = false;
|
||||
// Update global timestamps.
|
||||
const beforeMutationTs = getTimestamp();
|
||||
MUTATION[key] = [
|
||||
beforeMutationTs,
|
||||
0
|
||||
];
|
||||
const hasOptimisticData = !isUndefined(optimisticData);
|
||||
const state = get();
|
||||
// `displayedData` is the current value on screen. It could be the optimistic value
|
||||
// that is going to be overridden by a `committedData`, or get reverted back.
|
||||
// `committedData` is the validated value that comes from a fetch or mutation.
|
||||
const displayedData = state.data;
|
||||
const currentData = state._c;
|
||||
const committedData = isUndefined(currentData) ? displayedData : currentData;
|
||||
// Do optimistic data update.
|
||||
if (hasOptimisticData) {
|
||||
optimisticData = isFunction(optimisticData) ? optimisticData(committedData, displayedData) : optimisticData;
|
||||
// When we set optimistic data, backup the current committedData data in `_c`.
|
||||
set({
|
||||
data: optimisticData,
|
||||
_c: committedData
|
||||
});
|
||||
}
|
||||
if (isFunction(data)) {
|
||||
// `data` is a function, call it passing current cache value.
|
||||
try {
|
||||
data = data(committedData);
|
||||
} catch (err) {
|
||||
// If it throws an error synchronously, we shouldn't update the cache.
|
||||
error = err;
|
||||
isError = true;
|
||||
}
|
||||
}
|
||||
// `data` is a promise/thenable, resolve the final data first.
|
||||
if (data && isPromiseLike(data)) {
|
||||
// This means that the mutation is async, we need to check timestamps to
|
||||
// avoid race conditions.
|
||||
data = await data.catch((err)=>{
|
||||
error = err;
|
||||
isError = true;
|
||||
});
|
||||
// Check if other mutations have occurred since we've started this mutation.
|
||||
// If there's a race we don't update cache or broadcast the change,
|
||||
// just return the data.
|
||||
if (beforeMutationTs !== MUTATION[key][0]) {
|
||||
if (isError) throw error;
|
||||
return data;
|
||||
} else if (isError && hasOptimisticData && rollbackOnError(error)) {
|
||||
// Rollback. Always populate the cache in this case but without
|
||||
// transforming the data.
|
||||
populateCache = true;
|
||||
// Reset data to be the latest committed data, and clear the `_c` value.
|
||||
set({
|
||||
data: committedData,
|
||||
_c: UNDEFINED
|
||||
});
|
||||
}
|
||||
}
|
||||
// If we should write back the cache after request.
|
||||
if (populateCache) {
|
||||
if (!isError) {
|
||||
// Transform the result into data.
|
||||
if (isFunction(populateCache)) {
|
||||
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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// Reset the timestamp to mark the mutation has ended.
|
||||
MUTATION[key][1] = getTimestamp();
|
||||
// Update existing SWR Hooks' internal states:
|
||||
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 (isError) {
|
||||
if (throwOnError) throw error;
|
||||
return;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
const revalidateAllKeys = (revalidators, type)=>{
|
||||
for(const key in revalidators){
|
||||
if (revalidators[key][0]) revalidators[key][0](type);
|
||||
}
|
||||
};
|
||||
const initCache = (provider, options)=>{
|
||||
// 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
|
||||
// the cache.
|
||||
// The provider's global state might be already initialized. Let's try to get the
|
||||
// global state associated with the provider first.
|
||||
if (!SWRGlobalState.has(provider)) {
|
||||
const opts = mergeObjects(defaultConfigOptions, options);
|
||||
// If there's no global state bound to the provider, create a new one with the
|
||||
// new mutate function.
|
||||
const EVENT_REVALIDATORS = Object.create(null);
|
||||
const mutate = internalMutate.bind(UNDEFINED, provider);
|
||||
let unmount = noop;
|
||||
const subscriptions = Object.create(null);
|
||||
const subscribe = (key, callback)=>{
|
||||
const subs = subscriptions[key] || [];
|
||||
subscriptions[key] = subs;
|
||||
subs.push(callback);
|
||||
return ()=>{
|
||||
const index = subs.indexOf(callback);
|
||||
if (index >= 0) {
|
||||
// O(1): swap with the last element and pop
|
||||
subs[index] = subs[subs.length - 1];
|
||||
subs.pop();
|
||||
}
|
||||
};
|
||||
};
|
||||
const setter = (key, value, prev)=>{
|
||||
provider.set(key, value);
|
||||
const subs = subscriptions[key];
|
||||
if (subs) {
|
||||
for (const fn of subs){
|
||||
fn(value, prev);
|
||||
}
|
||||
}
|
||||
};
|
||||
const unload = (unloadOptions)=>{
|
||||
const state = SWRGlobalState.get(provider);
|
||||
const [, MUTATION, FETCH, PRELOAD] = state;
|
||||
const ts = getTimestamp();
|
||||
// Bump the unload generation so in-flight writes that bypass the
|
||||
// request markers (e.g. `useSWRInfinite` page responses) know to
|
||||
// discard themselves when they resolve.
|
||||
state[8]++;
|
||||
// Invalidate all in-flight requests and mutations first, so nothing
|
||||
// that resolves after this point can write back to the cache: fetches
|
||||
// fail the concurrent request marker check, and mutations fail the
|
||||
// timestamp race check. Mutation timestamps are overwritten instead of
|
||||
// deleted because ongoing mutations read them on resolution.
|
||||
for(const key in FETCH)delete FETCH[key];
|
||||
for(const key in PRELOAD)delete PRELOAD[key];
|
||||
for(const key in MUTATION)MUTATION[key] = [
|
||||
ts,
|
||||
ts
|
||||
];
|
||||
// Delete all entries — including the special `useSWRInfinite` and
|
||||
// `useSWRSubscription` keys — and notify the subscribers of each key
|
||||
// so mounted hooks re-render with the empty cache. Subscribers receive
|
||||
// the same empty state object that cache reads resolve to for missing
|
||||
// entries.
|
||||
const emptyState = {};
|
||||
for (const key of [
|
||||
...provider.keys()
|
||||
]){
|
||||
const prev = provider.get(key);
|
||||
provider.delete(key);
|
||||
const subs = subscriptions[key];
|
||||
if (subs) {
|
||||
for (const fn of subs){
|
||||
fn(emptyState, prev);
|
||||
}
|
||||
}
|
||||
}
|
||||
// The unload event runs last so the fresh requests it may start are
|
||||
// not invalidated by the cleanup above. It always fires — even without
|
||||
// revalidation — and to every hook on the key, so each one can drop
|
||||
// the previous data kept by `keepPreviousData`. Only the first hook
|
||||
// revalidates, consistent with how mutations revalidate a key.
|
||||
const revalidate = !unloadOptions || unloadOptions.revalidate !== false;
|
||||
for(const key in EVENT_REVALIDATORS){
|
||||
const revalidators = EVENT_REVALIDATORS[key];
|
||||
for(let i = 0; i < revalidators.length; i++){
|
||||
revalidators[i](UNLOAD_EVENT, {
|
||||
revalidate: revalidate && !i
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
const initProvider = ()=>{
|
||||
if (!SWRGlobalState.has(provider)) {
|
||||
// Update the state if it's new, or if the provider has been extended.
|
||||
SWRGlobalState.set(provider, [
|
||||
EVENT_REVALIDATORS,
|
||||
Object.create(null),
|
||||
Object.create(null),
|
||||
Object.create(null),
|
||||
mutate,
|
||||
setter,
|
||||
subscribe,
|
||||
unload,
|
||||
0
|
||||
]);
|
||||
if (!IS_SERVER) {
|
||||
// When listening to the native events for auto revalidations,
|
||||
// we intentionally put a delay (setTimeout) here to make sure they are
|
||||
// fired after immediate JavaScript executions, which can be
|
||||
// React's state updates.
|
||||
// This avoids some unnecessary revalidations such as
|
||||
// https://github.com/vercel/swr/issues/1680.
|
||||
const releaseFocus = opts.initFocus(setTimeout.bind(UNDEFINED, revalidateAllKeys.bind(UNDEFINED, EVENT_REVALIDATORS, FOCUS_EVENT)));
|
||||
const releaseReconnect = opts.initReconnect(setTimeout.bind(UNDEFINED, revalidateAllKeys.bind(UNDEFINED, EVENT_REVALIDATORS, RECONNECT_EVENT)));
|
||||
unmount = ()=>{
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
releaseFocus && releaseFocus();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
releaseReconnect && releaseReconnect();
|
||||
// When un-mounting, we need to remove the cache provider from the state
|
||||
// storage too because it's a side-effect. Otherwise, when re-mounting we
|
||||
// will not re-register those event listeners.
|
||||
SWRGlobalState.delete(provider);
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
initProvider();
|
||||
// This is a new provider, we need to initialize it and setup DOM events
|
||||
// listeners for `focus` and `reconnect` actions.
|
||||
// We might want to inject an extra layer on top of `provider` in the future,
|
||||
// such as key serialization, auto GC, etc.
|
||||
// For now, it's just a `Map` interface without any modifications.
|
||||
return [
|
||||
provider,
|
||||
mutate,
|
||||
initProvider,
|
||||
unmount,
|
||||
unload
|
||||
];
|
||||
}
|
||||
const state = SWRGlobalState.get(provider);
|
||||
return [
|
||||
provider,
|
||||
state[4],
|
||||
UNDEFINED,
|
||||
UNDEFINED,
|
||||
state[7]
|
||||
];
|
||||
};
|
||||
|
||||
// error retry
|
||||
const onErrorRetry = (_, __, config, revalidate, opts)=>{
|
||||
const maxRetryCount = config.errorRetryCount;
|
||||
const currentRetryCount = opts.retryCount;
|
||||
// Exponential backoff
|
||||
const timeout = ~~((Math.random() + 0.5) * (1 << (currentRetryCount < 8 ? currentRetryCount : 8))) * config.errorRetryInterval;
|
||||
if (!isUndefined(maxRetryCount) && currentRetryCount > maxRetryCount) {
|
||||
return;
|
||||
}
|
||||
setTimeout(revalidate, timeout, opts);
|
||||
};
|
||||
const compare = lite.dequal;
|
||||
// Default cache provider
|
||||
const [cache, mutate, , , unload] = initCache(new Map());
|
||||
// Default config
|
||||
const defaultConfig = mergeObjects({
|
||||
// events
|
||||
onLoadingSlow: noop,
|
||||
onSuccess: noop,
|
||||
onError: noop,
|
||||
onErrorRetry,
|
||||
onDiscarded: noop,
|
||||
// switches
|
||||
revalidateOnFocus: true,
|
||||
revalidateOnReconnect: true,
|
||||
revalidateIfStale: true,
|
||||
shouldRetryOnError: true,
|
||||
// timeouts
|
||||
errorRetryInterval: slowConnection ? 10000 : 5000,
|
||||
focusThrottleInterval: 5 * 1000,
|
||||
dedupingInterval: 2 * 1000,
|
||||
loadingTimeout: slowConnection ? 5000 : 3000,
|
||||
// providers
|
||||
compare,
|
||||
isPaused: ()=>false,
|
||||
cache,
|
||||
mutate,
|
||||
unload,
|
||||
fallback: {}
|
||||
}, // use web preset by default
|
||||
preset);
|
||||
|
||||
const mergeConfigs = (a, b)=>{
|
||||
// Need to create a new object to avoid mutating the original here.
|
||||
const v = mergeObjects(a, b);
|
||||
// If two configs are provided, merge their `use`, `fallback`, and
|
||||
// `cacheData` options.
|
||||
if (b) {
|
||||
const { use: u1, fallback: f1, cacheData: c1 } = a;
|
||||
const { use: u2, fallback: f2, cacheData: c2 } = b;
|
||||
if (u1 && u2) {
|
||||
v.use = u1.concat(u2);
|
||||
}
|
||||
if (f1 && f2) {
|
||||
v.fallback = mergeObjects(f1, f2);
|
||||
}
|
||||
if (c1 && c2) {
|
||||
v.cacheData = mergeObjects(c1, c2);
|
||||
}
|
||||
}
|
||||
return v;
|
||||
};
|
||||
|
||||
const SWRConfigContext = React.createContext({});
|
||||
const SWRConfig = (props)=>{
|
||||
const { value } = props;
|
||||
const parentConfig = React.useContext(SWRConfigContext);
|
||||
const isFunctionalConfig = isFunction(value);
|
||||
const config = React.useMemo(()=>isFunctionalConfig ? value(parentConfig) : value, [
|
||||
isFunctionalConfig,
|
||||
parentConfig,
|
||||
value
|
||||
]);
|
||||
// Extend parent context values and middleware.
|
||||
const extendedConfig = React.useMemo(()=>isFunctionalConfig ? config : mergeConfigs(parentConfig, config), [
|
||||
isFunctionalConfig,
|
||||
parentConfig,
|
||||
config
|
||||
]);
|
||||
// Should not use the inherited provider.
|
||||
const provider = config && config.provider;
|
||||
// initialize the cache only on first access.
|
||||
const cacheContextRef = React.useRef(UNDEFINED);
|
||||
if (provider && !cacheContextRef.current) {
|
||||
cacheContextRef.current = initCache(provider(extendedConfig.cache || cache), config);
|
||||
}
|
||||
const cacheContext = cacheContextRef.current;
|
||||
// Override the cache if a new provider is given.
|
||||
if (cacheContext) {
|
||||
extendedConfig.cache = cacheContext[0];
|
||||
extendedConfig.mutate = cacheContext[1];
|
||||
extendedConfig.unload = cacheContext[4];
|
||||
}
|
||||
// Unsubscribe events.
|
||||
useIsomorphicLayoutEffect(()=>{
|
||||
if (cacheContext) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
cacheContext[2] && cacheContext[2]();
|
||||
return cacheContext[3];
|
||||
}
|
||||
}, []);
|
||||
return React.createElement(SWRConfigContext.Provider, mergeObjects(props, {
|
||||
value: extendedConfig
|
||||
}));
|
||||
};
|
||||
|
||||
exports.ERROR_REVALIDATE_EVENT = ERROR_REVALIDATE_EVENT;
|
||||
exports.FOCUS_EVENT = FOCUS_EVENT;
|
||||
exports.IS_REACT_LEGACY = IS_REACT_LEGACY;
|
||||
exports.IS_SERVER = IS_SERVER;
|
||||
exports.MUTATE_EVENT = MUTATE_EVENT;
|
||||
exports.OBJECT = OBJECT;
|
||||
exports.RECONNECT_EVENT = RECONNECT_EVENT;
|
||||
exports.SWRConfig = SWRConfig;
|
||||
exports.SWRConfigContext = SWRConfigContext;
|
||||
exports.SWRGlobalState = SWRGlobalState;
|
||||
exports.UNDEFINED = UNDEFINED;
|
||||
exports.UNLOAD_EVENT = UNLOAD_EVENT;
|
||||
exports.cache = cache;
|
||||
exports.compare = compare;
|
||||
exports.createCacheHelper = createCacheHelper;
|
||||
exports.defaultConfig = defaultConfig;
|
||||
exports.defaultConfigOptions = defaultConfigOptions;
|
||||
exports.events = events;
|
||||
exports.getTimestamp = getTimestamp;
|
||||
exports.hasRequestAnimationFrame = hasRequestAnimationFrame;
|
||||
exports.initCache = initCache;
|
||||
exports.internalMutate = internalMutate;
|
||||
exports.isDocumentDefined = isDocumentDefined;
|
||||
exports.isFunction = isFunction;
|
||||
exports.isLegacyDeno = isLegacyDeno;
|
||||
exports.isPromiseLike = isPromiseLike;
|
||||
exports.isUndefined = isUndefined;
|
||||
exports.isWindowDefined = isWindowDefined;
|
||||
exports.mergeConfigs = mergeConfigs;
|
||||
exports.mergeObjects = mergeObjects;
|
||||
exports.mutate = mutate;
|
||||
exports.noop = noop;
|
||||
exports.preset = preset;
|
||||
exports.rAF = rAF;
|
||||
exports.serialize = serialize;
|
||||
exports.slowConnection = slowConnection;
|
||||
exports.stableHash = stableHash;
|
||||
exports.unload = unload;
|
||||
exports.useIsomorphicLayoutEffect = useIsomorphicLayoutEffect;
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
'use client';
|
||||
import { SWRConfig as SWRConfig$1 } from './_internal/index.mjs';
|
||||
|
||||
const SWRConfig = SWRConfig$1;
|
||||
|
||||
export { SWRConfig as S };
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
declare const FOCUS_EVENT = 0;
|
||||
declare const RECONNECT_EVENT = 1;
|
||||
declare const MUTATE_EVENT = 2;
|
||||
declare const ERROR_REVALIDATE_EVENT = 3;
|
||||
declare const UNLOAD_EVENT = 4;
|
||||
|
||||
declare const events_ERROR_REVALIDATE_EVENT: typeof ERROR_REVALIDATE_EVENT;
|
||||
declare const events_FOCUS_EVENT: typeof FOCUS_EVENT;
|
||||
declare const events_MUTATE_EVENT: typeof MUTATE_EVENT;
|
||||
declare const events_RECONNECT_EVENT: typeof RECONNECT_EVENT;
|
||||
declare const events_UNLOAD_EVENT: typeof UNLOAD_EVENT;
|
||||
declare namespace events {
|
||||
export {
|
||||
events_ERROR_REVALIDATE_EVENT as ERROR_REVALIDATE_EVENT,
|
||||
events_FOCUS_EVENT as FOCUS_EVENT,
|
||||
events_MUTATE_EVENT as MUTATE_EVENT,
|
||||
events_RECONNECT_EVENT as RECONNECT_EVENT,
|
||||
events_UNLOAD_EVENT as UNLOAD_EVENT,
|
||||
};
|
||||
}
|
||||
|
||||
export { ERROR_REVALIDATE_EVENT as E, FOCUS_EVENT as F, MUTATE_EVENT as M, RECONNECT_EVENT as R, UNLOAD_EVENT as U, events as e };
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import { a as SWRHook, M as Middleware } from '../types-l2s7dphv.js';
|
||||
|
||||
declare const immutable: Middleware;
|
||||
declare const useSWRImmutable: SWRHook;
|
||||
|
||||
export { useSWRImmutable as default, immutable };
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import { h as SWRHook, i as Middleware } from '../types-m1fld6v2.js';
|
||||
|
||||
declare const immutable: Middleware;
|
||||
declare const useSWRImmutable: SWRHook;
|
||||
|
||||
export { useSWRImmutable as default, immutable };
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
var useSwr = require('../use-swr-guqrv3k1.js');
|
||||
var _internal_index = require('../_internal/index.js');
|
||||
|
||||
const immutable = (useSWRNext)=>(key, fetcher, config)=>{
|
||||
// Always override all revalidate options.
|
||||
config.revalidateOnFocus = false;
|
||||
config.revalidateIfStale = false;
|
||||
config.revalidateOnReconnect = false;
|
||||
config.refreshInterval = 0;
|
||||
return useSWRNext(key, fetcher, config);
|
||||
};
|
||||
const useSWRImmutable = _internal_index.withMiddleware(useSwr.useSWR, immutable);
|
||||
|
||||
exports.default = useSWRImmutable;
|
||||
exports.immutable = immutable;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import { u as useSWR } from '../use-swr-emll9s78.mjs';
|
||||
import { withMiddleware } from '../_internal/index.mjs';
|
||||
|
||||
const immutable = (useSWRNext)=>(key, fetcher, config)=>{
|
||||
// Always override all revalidate options.
|
||||
config.revalidateOnFocus = false;
|
||||
config.revalidateIfStale = false;
|
||||
config.revalidateOnReconnect = false;
|
||||
config.refreshInterval = 0;
|
||||
return useSWRNext(key, fetcher, config);
|
||||
};
|
||||
const useSWRImmutable = withMiddleware(useSWR, immutable);
|
||||
|
||||
export { useSWRImmutable as default, immutable };
|
||||
+1
@@ -0,0 +1 @@
|
||||
export { A as Arguments, B as BareFetcher, h as Cache, C as CacheData, l as Fetcher, K as Key, p as KeyLoader, q as KeyedMutator, M as Middleware, d as MutatorCallback, g as MutatorOptions, y as Revalidator, z as RevalidatorOptions, V as SWRConfig, b as SWRConfigValue, e as SWRConfiguration, W as SWRGlobalConfig, a as SWRHook, c as SWRResponse, i as ScopedMutator, j as State, E as UnloadOptions, U as Unloader, X as default, N as mutate, O as preload, Q as unload, u as unstable_serialize, T as useSWRConfig } from '../types-l2s7dphv.js';
|
||||
+1
@@ -0,0 +1 @@
|
||||
export { A as Arguments, B as BareFetcher, a as Cache, C as CacheData, f as Fetcher, K as Key, m as KeyLoader, n as KeyedMutator, i as Middleware, M as MutatorCallback, e as MutatorOptions, u as Revalidator, v as RevalidatorOptions, T as SWRConfig, w as SWRConfigValue, g as SWRConfiguration, V as SWRGlobalConfig, h as SWRHook, x as SWRResponse, b as ScopedMutator, c as State, D as UnloadOptions, U as Unloader, W as default, L as mutate, N as preload, O as unload, X as unstable_serialize, Q as useSWRConfig } from '../types-m1fld6v2.js';
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
var useSwr = require('../use-swr-guqrv3k1.js');
|
||||
var bunchee_group_0 = require('../config-context-s960zqob.js');
|
||||
var _internal_index = require('../_internal/index.js');
|
||||
|
||||
const unstable_serialize = (key)=>bunchee_group_0.serialize(key)[0];
|
||||
|
||||
// useSWR
|
||||
|
||||
exports.SWRConfig = useSwr.SWRConfig;
|
||||
exports.default = useSwr.useSWR;
|
||||
exports.mutate = bunchee_group_0.mutate;
|
||||
exports.unload = bunchee_group_0.unload;
|
||||
exports.preload = _internal_index.preload;
|
||||
exports.useSWRConfig = _internal_index.useSWRConfig;
|
||||
exports.unstable_serialize = unstable_serialize;
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { u as useSWR } from '../use-swr-emll9s78.mjs';
|
||||
export { S as SWRConfig } from '../use-swr-emll9s78.mjs';
|
||||
import { s as serialize } from '../config-context-ext53wcz.mjs';
|
||||
export { k as mutate, l as unload } from '../config-context-ext53wcz.mjs';
|
||||
export { preload, useSWRConfig } from '../_internal/index.mjs';
|
||||
|
||||
const unstable_serialize = (key)=>serialize(key)[0];
|
||||
|
||||
// useSWR
|
||||
|
||||
export { useSWR as default, unstable_serialize };
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import { b as SWRConfigValue } from '../types-l2s7dphv.js';
|
||||
export { u as unstable_serialize } from '../types-l2s7dphv.js';
|
||||
export { p as preload } from '../server-preload-cqglm8k4.js';
|
||||
import * as react from 'react';
|
||||
|
||||
declare const SWRConfig: react.FC<react.PropsWithChildren<{
|
||||
value?: SWRConfigValue | ((parentConfig?: SWRConfigValue) => SWRConfigValue);
|
||||
}>>;
|
||||
|
||||
export { SWRConfig };
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import { s as serialize } from '../serialize-hqt5osie.mjs';
|
||||
export { p as preload } from '../server-preload-jludd90x.mjs';
|
||||
export { S as SWRConfig } from '../config-ggle1mjw.mjs';
|
||||
|
||||
const unstable_serialize = (key)=>serialize(key)[0];
|
||||
|
||||
export { unstable_serialize };
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
import { A as Arguments, B as BareFetcher, e as SWRConfiguration, f as StrictTupleKey, c as SWRResponse, d as MutatorCallback, g as MutatorOptions, M as Middleware } from '../types-l2s7dphv.js';
|
||||
|
||||
type FetcherResponse<Data = unknown> = Data | Promise<Data>;
|
||||
type SWRInfiniteFetcher<Data = any, KeyLoader extends SWRInfiniteKeyLoader = SWRInfiniteKeyLoader> = KeyLoader extends (...args: any[]) => any ? ReturnType<KeyLoader> extends (infer T) | null | false | undefined ? (args: T) => FetcherResponse<Data> : never : never;
|
||||
type SWRInfiniteKeyLoader<Data = any, Args extends Arguments = Arguments> = (index: number, previousPageData: Data | null) => Args;
|
||||
interface SWRInfiniteCompareFn<Data = any> {
|
||||
(a: Data | undefined, b: Data | undefined): boolean;
|
||||
(a: Data[] | undefined, b: Data[] | undefined): boolean;
|
||||
}
|
||||
interface SWRInfiniteConfiguration<Data = any, Error = any, Fn extends SWRInfiniteFetcher<Data> = BareFetcher<Data>> extends Omit<SWRConfiguration<Data[], Error>, 'compare'> {
|
||||
initialSize?: number;
|
||||
revalidateAll?: boolean;
|
||||
persistSize?: boolean;
|
||||
revalidateFirstPage?: boolean;
|
||||
parallel?: boolean;
|
||||
fetcher?: Fn;
|
||||
compare?: SWRInfiniteCompareFn<Data>;
|
||||
}
|
||||
interface SWRInfiniteRevalidateFn<Data = any> {
|
||||
(data: Data, key: Arguments): boolean;
|
||||
}
|
||||
type SWRInfiniteKeyedMutator<Data> = <MutationData = Data>(data?: Data | Promise<Data | undefined> | MutatorCallback<Data>, opts?: boolean | SWRInfiniteMutatorOptions<Data, MutationData>) => Promise<Data | MutationData | undefined>;
|
||||
interface SWRInfiniteMutatorOptions<Data = any, MutationData = Data> extends Omit<MutatorOptions<Data, MutationData>, 'revalidate'> {
|
||||
revalidate?: boolean | SWRInfiniteRevalidateFn<Data extends unknown[] ? Data[number] : never>;
|
||||
}
|
||||
interface SWRInfiniteResponse<Data = any, Error = any> extends Omit<SWRResponse<Data[], Error>, 'mutate'> {
|
||||
size: number;
|
||||
setSize: (size: number | ((_size: number) => number)) => Promise<Data[] | undefined>;
|
||||
mutate: SWRInfiniteKeyedMutator<Data[]>;
|
||||
}
|
||||
interface SWRInfiniteHook {
|
||||
<Data = any, Error = any, KeyLoader extends SWRInfiniteKeyLoader = (index: number, previousPageData: Data | null) => StrictTupleKey>(getKey: KeyLoader): SWRInfiniteResponse<Data, Error>;
|
||||
<Data = any, Error = any, KeyLoader extends SWRInfiniteKeyLoader = (index: number, previousPageData: Data | null) => StrictTupleKey>(getKey: KeyLoader, fetcher: SWRInfiniteFetcher<Data, KeyLoader> | null): SWRInfiniteResponse<Data, Error>;
|
||||
<Data = any, Error = any, KeyLoader extends SWRInfiniteKeyLoader = (index: number, previousPageData: Data | null) => StrictTupleKey>(getKey: KeyLoader, config: SWRInfiniteConfiguration<Data, Error, SWRInfiniteFetcher<Data, KeyLoader>> | undefined): SWRInfiniteResponse<Data, Error>;
|
||||
<Data = any, Error = any, KeyLoader extends SWRInfiniteKeyLoader = (index: number, previousPageData: Data | null) => StrictTupleKey>(getKey: KeyLoader, fetcher: SWRInfiniteFetcher<Data, KeyLoader> | null, config: SWRInfiniteConfiguration<Data, Error, SWRInfiniteFetcher<Data, KeyLoader>> | undefined): SWRInfiniteResponse<Data, Error>;
|
||||
<Data = any, Error = any>(getKey: SWRInfiniteKeyLoader): SWRInfiniteResponse<Data, Error>;
|
||||
<Data = any, Error = any>(getKey: SWRInfiniteKeyLoader, fetcher: BareFetcher<Data> | null): SWRInfiniteResponse<Data, Error>;
|
||||
<Data = any, Error = any>(getKey: SWRInfiniteKeyLoader, config: SWRInfiniteConfiguration<Data, Error, BareFetcher<Data>> | undefined): SWRInfiniteResponse<Data, Error>;
|
||||
<Data = any, Error = any>(getKey: SWRInfiniteKeyLoader, fetcher: BareFetcher<Data> | null, config: SWRInfiniteConfiguration<Data, Error, BareFetcher<Data>> | undefined): SWRInfiniteResponse<Data, Error>;
|
||||
}
|
||||
|
||||
declare const unstable_serialize: (getKey: SWRInfiniteKeyLoader) => string;
|
||||
|
||||
declare const infinite: Middleware;
|
||||
declare const useSWRInfinite: SWRInfiniteHook;
|
||||
|
||||
export { useSWRInfinite as default, infinite, unstable_serialize };
|
||||
export type { SWRInfiniteCompareFn, SWRInfiniteConfiguration, SWRInfiniteFetcher, SWRInfiniteHook, SWRInfiniteKeyLoader, SWRInfiniteKeyedMutator, SWRInfiniteMutatorOptions, SWRInfiniteResponse };
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
import { A as Arguments, B as BareFetcher, g as SWRConfiguration, z as StrictTupleKey, x as SWRResponse, M as MutatorCallback, e as MutatorOptions, i as Middleware } from '../types-m1fld6v2.js';
|
||||
|
||||
type FetcherResponse<Data = unknown> = Data | Promise<Data>;
|
||||
type SWRInfiniteFetcher<Data = any, KeyLoader extends SWRInfiniteKeyLoader = SWRInfiniteKeyLoader> = KeyLoader extends (...args: any[]) => any ? ReturnType<KeyLoader> extends (infer T) | null | false | undefined ? (args: T) => FetcherResponse<Data> : never : never;
|
||||
type SWRInfiniteKeyLoader<Data = any, Args extends Arguments = Arguments> = (index: number, previousPageData: Data | null) => Args;
|
||||
interface SWRInfiniteCompareFn<Data = any> {
|
||||
(a: Data | undefined, b: Data | undefined): boolean;
|
||||
(a: Data[] | undefined, b: Data[] | undefined): boolean;
|
||||
}
|
||||
interface SWRInfiniteConfiguration<Data = any, Error = any, Fn extends SWRInfiniteFetcher<Data> = BareFetcher<Data>> extends Omit<SWRConfiguration<Data[], Error>, 'compare'> {
|
||||
initialSize?: number;
|
||||
revalidateAll?: boolean;
|
||||
persistSize?: boolean;
|
||||
revalidateFirstPage?: boolean;
|
||||
parallel?: boolean;
|
||||
fetcher?: Fn;
|
||||
compare?: SWRInfiniteCompareFn<Data>;
|
||||
}
|
||||
interface SWRInfiniteRevalidateFn<Data = any> {
|
||||
(data: Data, key: Arguments): boolean;
|
||||
}
|
||||
type SWRInfiniteKeyedMutator<Data> = <MutationData = Data>(data?: Data | Promise<Data | undefined> | MutatorCallback<Data>, opts?: boolean | SWRInfiniteMutatorOptions<Data, MutationData>) => Promise<Data | MutationData | undefined>;
|
||||
interface SWRInfiniteMutatorOptions<Data = any, MutationData = Data> extends Omit<MutatorOptions<Data, MutationData>, 'revalidate'> {
|
||||
revalidate?: boolean | SWRInfiniteRevalidateFn<Data extends unknown[] ? Data[number] : never>;
|
||||
}
|
||||
interface SWRInfiniteResponse<Data = any, Error = any> extends Omit<SWRResponse<Data[], Error>, 'mutate'> {
|
||||
size: number;
|
||||
setSize: (size: number | ((_size: number) => number)) => Promise<Data[] | undefined>;
|
||||
mutate: SWRInfiniteKeyedMutator<Data[]>;
|
||||
}
|
||||
interface SWRInfiniteHook {
|
||||
<Data = any, Error = any, KeyLoader extends SWRInfiniteKeyLoader = (index: number, previousPageData: Data | null) => StrictTupleKey>(getKey: KeyLoader): SWRInfiniteResponse<Data, Error>;
|
||||
<Data = any, Error = any, KeyLoader extends SWRInfiniteKeyLoader = (index: number, previousPageData: Data | null) => StrictTupleKey>(getKey: KeyLoader, fetcher: SWRInfiniteFetcher<Data, KeyLoader> | null): SWRInfiniteResponse<Data, Error>;
|
||||
<Data = any, Error = any, KeyLoader extends SWRInfiniteKeyLoader = (index: number, previousPageData: Data | null) => StrictTupleKey>(getKey: KeyLoader, config: SWRInfiniteConfiguration<Data, Error, SWRInfiniteFetcher<Data, KeyLoader>> | undefined): SWRInfiniteResponse<Data, Error>;
|
||||
<Data = any, Error = any, KeyLoader extends SWRInfiniteKeyLoader = (index: number, previousPageData: Data | null) => StrictTupleKey>(getKey: KeyLoader, fetcher: SWRInfiniteFetcher<Data, KeyLoader> | null, config: SWRInfiniteConfiguration<Data, Error, SWRInfiniteFetcher<Data, KeyLoader>> | undefined): SWRInfiniteResponse<Data, Error>;
|
||||
<Data = any, Error = any>(getKey: SWRInfiniteKeyLoader): SWRInfiniteResponse<Data, Error>;
|
||||
<Data = any, Error = any>(getKey: SWRInfiniteKeyLoader, fetcher: BareFetcher<Data> | null): SWRInfiniteResponse<Data, Error>;
|
||||
<Data = any, Error = any>(getKey: SWRInfiniteKeyLoader, config: SWRInfiniteConfiguration<Data, Error, BareFetcher<Data>> | undefined): SWRInfiniteResponse<Data, Error>;
|
||||
<Data = any, Error = any>(getKey: SWRInfiniteKeyLoader, fetcher: BareFetcher<Data> | null, config: SWRInfiniteConfiguration<Data, Error, BareFetcher<Data>> | undefined): SWRInfiniteResponse<Data, Error>;
|
||||
}
|
||||
|
||||
declare const unstable_serialize: (getKey: SWRInfiniteKeyLoader) => string;
|
||||
|
||||
declare const infinite: Middleware;
|
||||
declare const useSWRInfinite: SWRInfiniteHook;
|
||||
|
||||
export { useSWRInfinite as default, infinite, unstable_serialize };
|
||||
export type { SWRInfiniteCompareFn, SWRInfiniteConfiguration, SWRInfiniteFetcher, SWRInfiniteHook, SWRInfiniteKeyLoader, SWRInfiniteKeyedMutator, SWRInfiniteMutatorOptions, SWRInfiniteResponse };
|
||||
+270
@@ -0,0 +1,270 @@
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
var React = require('react');
|
||||
var useSwr = require('../use-swr-guqrv3k1.js');
|
||||
var bunchee_group_0 = require('../config-context-s960zqob.js');
|
||||
var _internal_index = require('../_internal/index.js');
|
||||
var shim = require('use-sync-external-store/shim');
|
||||
var _internal_constants = require('../_internal/constants.js');
|
||||
|
||||
const getFirstPageKey = (getKey)=>{
|
||||
return bunchee_group_0.serialize(getKey ? getKey(0, null) : null)[0];
|
||||
};
|
||||
const unstable_serialize = (getKey)=>{
|
||||
return _internal_constants.INFINITE_PREFIX + getFirstPageKey(getKey);
|
||||
};
|
||||
|
||||
// We have to several type castings here because `useSWRInfinite` is a special
|
||||
// hook where `key` and return type are not like the normal `useSWR` types.
|
||||
const EMPTY_PROMISE = Promise.resolve();
|
||||
const infinite = (useSWRNext)=>(getKey, fn, config)=>{
|
||||
const didMountRef = React.useRef(false);
|
||||
const { cache, initialSize = 1, revalidateAll = false, persistSize = false, revalidateFirstPage = true, revalidateOnMount = false, parallel = false } = config;
|
||||
const [, , , PRELOAD] = bunchee_group_0.SWRGlobalState.get(bunchee_group_0.cache);
|
||||
// The serialized key of the first page. This key will be used to store
|
||||
// metadata of this SWR infinite hook.
|
||||
let infiniteKey;
|
||||
try {
|
||||
infiniteKey = getFirstPageKey(getKey);
|
||||
if (infiniteKey) infiniteKey = _internal_constants.INFINITE_PREFIX + infiniteKey;
|
||||
} catch (err) {
|
||||
// Not ready yet.
|
||||
}
|
||||
const [get, set, subscribeCache] = bunchee_group_0.createCacheHelper(cache, infiniteKey);
|
||||
const getSnapshot = React.useCallback(()=>{
|
||||
const size = bunchee_group_0.isUndefined(get()._l) ? initialSize : get()._l;
|
||||
return size;
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [
|
||||
cache,
|
||||
infiniteKey,
|
||||
initialSize
|
||||
]);
|
||||
shim.useSyncExternalStore(React.useCallback((callback)=>{
|
||||
if (infiniteKey) return subscribeCache(infiniteKey, ()=>{
|
||||
callback();
|
||||
});
|
||||
return ()=>{};
|
||||
}, // eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[
|
||||
cache,
|
||||
infiniteKey
|
||||
]), getSnapshot, getSnapshot);
|
||||
const resolvePageSize = React.useCallback(()=>{
|
||||
const cachedPageSize = get()._l;
|
||||
return bunchee_group_0.isUndefined(cachedPageSize) ? initialSize : cachedPageSize;
|
||||
// `cache` isn't allowed to change during the lifecycle
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [
|
||||
infiniteKey,
|
||||
initialSize
|
||||
]);
|
||||
// keep the last page size to restore it with the persistSize option
|
||||
const lastPageSizeRef = React.useRef(resolvePageSize());
|
||||
// When the page key changes, we reset the page size if it's not persisted
|
||||
bunchee_group_0.useIsomorphicLayoutEffect(()=>{
|
||||
if (!didMountRef.current) {
|
||||
didMountRef.current = true;
|
||||
return;
|
||||
}
|
||||
if (infiniteKey) {
|
||||
// If the key has been changed, we keep the current page size if persistSize is enabled
|
||||
// Otherwise, we reset the page size to cached pageSize
|
||||
set({
|
||||
_l: persistSize ? lastPageSizeRef.current : resolvePageSize()
|
||||
});
|
||||
}
|
||||
// `initialSize` isn't allowed to change during the lifecycle
|
||||
}, [
|
||||
infiniteKey,
|
||||
cache
|
||||
]);
|
||||
// Needs to check didMountRef during mounting, not in the fetcher
|
||||
const shouldRevalidateOnMount = revalidateOnMount && !didMountRef.current;
|
||||
// Actual SWR hook to load all pages in one fetcher.
|
||||
const swr = useSWRNext(infiniteKey, async (key)=>{
|
||||
// get the revalidate context
|
||||
const forceRevalidateAll = get()._i;
|
||||
const shouldRevalidatePage = get()._r;
|
||||
// Capture the cache's unload generation (bumped by every `unload()`
|
||||
// call) so page responses that resolve after an unload are not
|
||||
// written back to the cleared cache.
|
||||
const state = bunchee_group_0.SWRGlobalState.get(cache);
|
||||
const unloadGeneration = state[8];
|
||||
const unloadedDuringRequest = ()=>state[8] !== unloadGeneration;
|
||||
set({
|
||||
_r: bunchee_group_0.UNDEFINED
|
||||
});
|
||||
// return an array of page data
|
||||
const data = [];
|
||||
const pageSize = resolvePageSize();
|
||||
const [getCache] = bunchee_group_0.createCacheHelper(cache, key);
|
||||
const cacheData = getCache().data;
|
||||
const revalidators = [];
|
||||
let previousPageData = null;
|
||||
for(let i = 0; i < pageSize; ++i){
|
||||
const [pageKey, pageArg] = bunchee_group_0.serialize(getKey(i, parallel ? null : previousPageData));
|
||||
if (!pageKey) {
|
||||
break;
|
||||
}
|
||||
const [getSWRCache, setSWRCache] = bunchee_group_0.createCacheHelper(cache, pageKey);
|
||||
// Get the cached page data.
|
||||
let pageData = getSWRCache().data;
|
||||
// should fetch (or revalidate) if:
|
||||
// - `revalidateAll` is enabled
|
||||
// - `mutate()` called
|
||||
// - the cache is missing
|
||||
// - it's the first page and it's not the initial render
|
||||
// - `revalidateOnMount` is enabled and it's on mount
|
||||
// - cache for that page has changed
|
||||
const shouldFetchPage = revalidateAll || forceRevalidateAll || bunchee_group_0.isUndefined(pageData) || revalidateFirstPage && !i && !bunchee_group_0.isUndefined(cacheData) || shouldRevalidateOnMount || cacheData && !bunchee_group_0.isUndefined(cacheData[i]) && !config.compare(cacheData[i], pageData);
|
||||
if (fn && (typeof shouldRevalidatePage === 'function' ? shouldRevalidatePage(pageData, pageArg) : shouldFetchPage)) {
|
||||
const revalidate = async ()=>{
|
||||
const hasPreloadedRequest = pageKey in PRELOAD;
|
||||
if (!hasPreloadedRequest) {
|
||||
pageData = await fn(pageArg);
|
||||
} else {
|
||||
const req = PRELOAD[pageKey];
|
||||
// delete the preload cache key before resolving it
|
||||
// in case there's an error
|
||||
delete PRELOAD[pageKey];
|
||||
// get the page data from the preload cache
|
||||
pageData = await req;
|
||||
}
|
||||
if (!unloadedDuringRequest()) {
|
||||
setSWRCache({
|
||||
data: pageData,
|
||||
_k: pageArg
|
||||
});
|
||||
}
|
||||
data[i] = pageData;
|
||||
};
|
||||
if (parallel) {
|
||||
revalidators.push(revalidate);
|
||||
} else {
|
||||
await revalidate();
|
||||
}
|
||||
} else {
|
||||
data[i] = pageData;
|
||||
}
|
||||
if (!parallel) {
|
||||
previousPageData = pageData;
|
||||
}
|
||||
}
|
||||
// flush all revalidateions in parallel
|
||||
if (parallel) {
|
||||
await Promise.all(revalidators.map((r)=>r()));
|
||||
}
|
||||
// once we executed the data fetching based on the context, clear the
|
||||
// context. Skipped after an unload so the cleared meta entry isn't
|
||||
// re-created.
|
||||
if (!unloadedDuringRequest()) {
|
||||
set({
|
||||
_i: bunchee_group_0.UNDEFINED
|
||||
});
|
||||
}
|
||||
// return the data
|
||||
return data;
|
||||
}, config);
|
||||
const mutate = React.useCallback(// eslint-disable-next-line func-names
|
||||
function(data, opts) {
|
||||
// When passing as a boolean, it's explicitly used to disable/enable
|
||||
// revalidation.
|
||||
const options = typeof opts === 'boolean' ? {
|
||||
revalidate: opts
|
||||
} : opts || {};
|
||||
// Default to true.
|
||||
const shouldRevalidate = options.revalidate !== false;
|
||||
// It is possible that the key is still falsy.
|
||||
if (!infiniteKey) return EMPTY_PROMISE;
|
||||
if (shouldRevalidate) {
|
||||
if (!bunchee_group_0.isUndefined(data)) {
|
||||
// We only revalidate the pages that are changed
|
||||
set({
|
||||
_i: false,
|
||||
_r: options.revalidate
|
||||
});
|
||||
} else {
|
||||
// Calling `mutate()`, we revalidate all pages
|
||||
set({
|
||||
_i: true,
|
||||
_r: options.revalidate
|
||||
});
|
||||
}
|
||||
}
|
||||
return arguments.length ? swr.mutate(data, {
|
||||
...options,
|
||||
revalidate: shouldRevalidate
|
||||
}) : swr.mutate();
|
||||
}, // swr.mutate is always the same reference
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[
|
||||
infiniteKey,
|
||||
cache
|
||||
]);
|
||||
// Extend the SWR API
|
||||
const setSize = React.useCallback((arg)=>{
|
||||
// It is possible that the key is still falsy.
|
||||
if (!infiniteKey) return EMPTY_PROMISE;
|
||||
const [, changeSize] = bunchee_group_0.createCacheHelper(cache, infiniteKey);
|
||||
let size;
|
||||
if (bunchee_group_0.isFunction(arg)) {
|
||||
size = arg(resolvePageSize());
|
||||
} else if (typeof arg == 'number') {
|
||||
size = arg;
|
||||
}
|
||||
if (typeof size != 'number') return EMPTY_PROMISE;
|
||||
changeSize({
|
||||
_l: size
|
||||
});
|
||||
lastPageSizeRef.current = size;
|
||||
// Calculate the page data after the size change.
|
||||
const data = [];
|
||||
const [getInfiniteCache] = bunchee_group_0.createCacheHelper(cache, infiniteKey);
|
||||
let previousPageData = null;
|
||||
for(let i = 0; i < size; ++i){
|
||||
const [pageKey] = bunchee_group_0.serialize(getKey(i, previousPageData));
|
||||
const [getCache] = bunchee_group_0.createCacheHelper(cache, pageKey);
|
||||
// Get the cached page data.
|
||||
const pageData = pageKey ? getCache().data : bunchee_group_0.UNDEFINED;
|
||||
// Call `mutate` with infinte cache data if we can't get it from the page cache.
|
||||
if (bunchee_group_0.isUndefined(pageData)) {
|
||||
return mutate(getInfiniteCache().data);
|
||||
}
|
||||
data.push(pageData);
|
||||
previousPageData = pageData;
|
||||
}
|
||||
return mutate(data);
|
||||
}, // exclude getKey from the dependencies, which isn't allowed to change during the lifecycle
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[
|
||||
infiniteKey,
|
||||
cache,
|
||||
mutate,
|
||||
resolvePageSize
|
||||
]);
|
||||
// Use getter functions to avoid unnecessary re-renders caused by triggering
|
||||
// all the getters of the returned swr object.
|
||||
return {
|
||||
size: resolvePageSize(),
|
||||
setSize,
|
||||
mutate,
|
||||
get data () {
|
||||
return swr.data;
|
||||
},
|
||||
get error () {
|
||||
return swr.error;
|
||||
},
|
||||
get isValidating () {
|
||||
return swr.isValidating;
|
||||
},
|
||||
get isLoading () {
|
||||
return swr.isLoading;
|
||||
}
|
||||
};
|
||||
};
|
||||
const useSWRInfinite = _internal_index.withMiddleware(useSwr.useSWR, infinite);
|
||||
|
||||
exports.default = useSWRInfinite;
|
||||
exports.infinite = infinite;
|
||||
exports.unstable_serialize = unstable_serialize;
|
||||
+266
@@ -0,0 +1,266 @@
|
||||
import { useRef, useCallback } from 'react';
|
||||
import { u as useSWR } from '../use-swr-emll9s78.mjs';
|
||||
import { s as serialize, a as SWRGlobalState, o as cache, c as createCacheHelper, i as isUndefined, u as useIsomorphicLayoutEffect, U as UNDEFINED, e as isFunction } from '../config-context-ext53wcz.mjs';
|
||||
import { withMiddleware } from '../_internal/index.mjs';
|
||||
import { useSyncExternalStore } from 'use-sync-external-store/shim';
|
||||
import { INFINITE_PREFIX } from '../_internal/constants.mjs';
|
||||
|
||||
const getFirstPageKey = (getKey)=>{
|
||||
return serialize(getKey ? getKey(0, null) : null)[0];
|
||||
};
|
||||
const unstable_serialize = (getKey)=>{
|
||||
return INFINITE_PREFIX + getFirstPageKey(getKey);
|
||||
};
|
||||
|
||||
// We have to several type castings here because `useSWRInfinite` is a special
|
||||
// hook where `key` and return type are not like the normal `useSWR` types.
|
||||
const EMPTY_PROMISE = Promise.resolve();
|
||||
const infinite = (useSWRNext)=>(getKey, fn, config)=>{
|
||||
const didMountRef = useRef(false);
|
||||
const { cache: cache$1, initialSize = 1, revalidateAll = false, persistSize = false, revalidateFirstPage = true, revalidateOnMount = false, parallel = false } = config;
|
||||
const [, , , PRELOAD] = SWRGlobalState.get(cache);
|
||||
// The serialized key of the first page. This key will be used to store
|
||||
// metadata of this SWR infinite hook.
|
||||
let infiniteKey;
|
||||
try {
|
||||
infiniteKey = getFirstPageKey(getKey);
|
||||
if (infiniteKey) infiniteKey = INFINITE_PREFIX + infiniteKey;
|
||||
} catch (err) {
|
||||
// Not ready yet.
|
||||
}
|
||||
const [get, set, subscribeCache] = createCacheHelper(cache$1, infiniteKey);
|
||||
const getSnapshot = useCallback(()=>{
|
||||
const size = isUndefined(get()._l) ? initialSize : get()._l;
|
||||
return size;
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [
|
||||
cache$1,
|
||||
infiniteKey,
|
||||
initialSize
|
||||
]);
|
||||
useSyncExternalStore(useCallback((callback)=>{
|
||||
if (infiniteKey) return subscribeCache(infiniteKey, ()=>{
|
||||
callback();
|
||||
});
|
||||
return ()=>{};
|
||||
}, // eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[
|
||||
cache$1,
|
||||
infiniteKey
|
||||
]), getSnapshot, getSnapshot);
|
||||
const resolvePageSize = useCallback(()=>{
|
||||
const cachedPageSize = get()._l;
|
||||
return isUndefined(cachedPageSize) ? initialSize : cachedPageSize;
|
||||
// `cache` isn't allowed to change during the lifecycle
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [
|
||||
infiniteKey,
|
||||
initialSize
|
||||
]);
|
||||
// keep the last page size to restore it with the persistSize option
|
||||
const lastPageSizeRef = useRef(resolvePageSize());
|
||||
// When the page key changes, we reset the page size if it's not persisted
|
||||
useIsomorphicLayoutEffect(()=>{
|
||||
if (!didMountRef.current) {
|
||||
didMountRef.current = true;
|
||||
return;
|
||||
}
|
||||
if (infiniteKey) {
|
||||
// If the key has been changed, we keep the current page size if persistSize is enabled
|
||||
// Otherwise, we reset the page size to cached pageSize
|
||||
set({
|
||||
_l: persistSize ? lastPageSizeRef.current : resolvePageSize()
|
||||
});
|
||||
}
|
||||
// `initialSize` isn't allowed to change during the lifecycle
|
||||
}, [
|
||||
infiniteKey,
|
||||
cache$1
|
||||
]);
|
||||
// Needs to check didMountRef during mounting, not in the fetcher
|
||||
const shouldRevalidateOnMount = revalidateOnMount && !didMountRef.current;
|
||||
// Actual SWR hook to load all pages in one fetcher.
|
||||
const swr = useSWRNext(infiniteKey, async (key)=>{
|
||||
// get the revalidate context
|
||||
const forceRevalidateAll = get()._i;
|
||||
const shouldRevalidatePage = get()._r;
|
||||
// Capture the cache's unload generation (bumped by every `unload()`
|
||||
// call) so page responses that resolve after an unload are not
|
||||
// written back to the cleared cache.
|
||||
const state = SWRGlobalState.get(cache$1);
|
||||
const unloadGeneration = state[8];
|
||||
const unloadedDuringRequest = ()=>state[8] !== unloadGeneration;
|
||||
set({
|
||||
_r: UNDEFINED
|
||||
});
|
||||
// return an array of page data
|
||||
const data = [];
|
||||
const pageSize = resolvePageSize();
|
||||
const [getCache] = createCacheHelper(cache$1, key);
|
||||
const cacheData = getCache().data;
|
||||
const revalidators = [];
|
||||
let previousPageData = null;
|
||||
for(let i = 0; i < pageSize; ++i){
|
||||
const [pageKey, pageArg] = serialize(getKey(i, parallel ? null : previousPageData));
|
||||
if (!pageKey) {
|
||||
break;
|
||||
}
|
||||
const [getSWRCache, setSWRCache] = createCacheHelper(cache$1, pageKey);
|
||||
// Get the cached page data.
|
||||
let pageData = getSWRCache().data;
|
||||
// should fetch (or revalidate) if:
|
||||
// - `revalidateAll` is enabled
|
||||
// - `mutate()` called
|
||||
// - the cache is missing
|
||||
// - it's the first page and it's not the initial render
|
||||
// - `revalidateOnMount` is enabled and it's on mount
|
||||
// - cache for that page has changed
|
||||
const shouldFetchPage = revalidateAll || forceRevalidateAll || isUndefined(pageData) || revalidateFirstPage && !i && !isUndefined(cacheData) || shouldRevalidateOnMount || cacheData && !isUndefined(cacheData[i]) && !config.compare(cacheData[i], pageData);
|
||||
if (fn && (typeof shouldRevalidatePage === 'function' ? shouldRevalidatePage(pageData, pageArg) : shouldFetchPage)) {
|
||||
const revalidate = async ()=>{
|
||||
const hasPreloadedRequest = pageKey in PRELOAD;
|
||||
if (!hasPreloadedRequest) {
|
||||
pageData = await fn(pageArg);
|
||||
} else {
|
||||
const req = PRELOAD[pageKey];
|
||||
// delete the preload cache key before resolving it
|
||||
// in case there's an error
|
||||
delete PRELOAD[pageKey];
|
||||
// get the page data from the preload cache
|
||||
pageData = await req;
|
||||
}
|
||||
if (!unloadedDuringRequest()) {
|
||||
setSWRCache({
|
||||
data: pageData,
|
||||
_k: pageArg
|
||||
});
|
||||
}
|
||||
data[i] = pageData;
|
||||
};
|
||||
if (parallel) {
|
||||
revalidators.push(revalidate);
|
||||
} else {
|
||||
await revalidate();
|
||||
}
|
||||
} else {
|
||||
data[i] = pageData;
|
||||
}
|
||||
if (!parallel) {
|
||||
previousPageData = pageData;
|
||||
}
|
||||
}
|
||||
// flush all revalidateions in parallel
|
||||
if (parallel) {
|
||||
await Promise.all(revalidators.map((r)=>r()));
|
||||
}
|
||||
// once we executed the data fetching based on the context, clear the
|
||||
// context. Skipped after an unload so the cleared meta entry isn't
|
||||
// re-created.
|
||||
if (!unloadedDuringRequest()) {
|
||||
set({
|
||||
_i: UNDEFINED
|
||||
});
|
||||
}
|
||||
// return the data
|
||||
return data;
|
||||
}, config);
|
||||
const mutate = useCallback(// eslint-disable-next-line func-names
|
||||
function(data, opts) {
|
||||
// When passing as a boolean, it's explicitly used to disable/enable
|
||||
// revalidation.
|
||||
const options = typeof opts === 'boolean' ? {
|
||||
revalidate: opts
|
||||
} : opts || {};
|
||||
// Default to true.
|
||||
const shouldRevalidate = options.revalidate !== false;
|
||||
// It is possible that the key is still falsy.
|
||||
if (!infiniteKey) return EMPTY_PROMISE;
|
||||
if (shouldRevalidate) {
|
||||
if (!isUndefined(data)) {
|
||||
// We only revalidate the pages that are changed
|
||||
set({
|
||||
_i: false,
|
||||
_r: options.revalidate
|
||||
});
|
||||
} else {
|
||||
// Calling `mutate()`, we revalidate all pages
|
||||
set({
|
||||
_i: true,
|
||||
_r: options.revalidate
|
||||
});
|
||||
}
|
||||
}
|
||||
return arguments.length ? swr.mutate(data, {
|
||||
...options,
|
||||
revalidate: shouldRevalidate
|
||||
}) : swr.mutate();
|
||||
}, // swr.mutate is always the same reference
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[
|
||||
infiniteKey,
|
||||
cache$1
|
||||
]);
|
||||
// Extend the SWR API
|
||||
const setSize = useCallback((arg)=>{
|
||||
// It is possible that the key is still falsy.
|
||||
if (!infiniteKey) return EMPTY_PROMISE;
|
||||
const [, changeSize] = createCacheHelper(cache$1, infiniteKey);
|
||||
let size;
|
||||
if (isFunction(arg)) {
|
||||
size = arg(resolvePageSize());
|
||||
} else if (typeof arg == 'number') {
|
||||
size = arg;
|
||||
}
|
||||
if (typeof size != 'number') return EMPTY_PROMISE;
|
||||
changeSize({
|
||||
_l: size
|
||||
});
|
||||
lastPageSizeRef.current = size;
|
||||
// Calculate the page data after the size change.
|
||||
const data = [];
|
||||
const [getInfiniteCache] = createCacheHelper(cache$1, infiniteKey);
|
||||
let previousPageData = null;
|
||||
for(let i = 0; i < size; ++i){
|
||||
const [pageKey] = serialize(getKey(i, previousPageData));
|
||||
const [getCache] = createCacheHelper(cache$1, pageKey);
|
||||
// Get the cached page data.
|
||||
const pageData = pageKey ? getCache().data : UNDEFINED;
|
||||
// Call `mutate` with infinte cache data if we can't get it from the page cache.
|
||||
if (isUndefined(pageData)) {
|
||||
return mutate(getInfiniteCache().data);
|
||||
}
|
||||
data.push(pageData);
|
||||
previousPageData = pageData;
|
||||
}
|
||||
return mutate(data);
|
||||
}, // exclude getKey from the dependencies, which isn't allowed to change during the lifecycle
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[
|
||||
infiniteKey,
|
||||
cache$1,
|
||||
mutate,
|
||||
resolvePageSize
|
||||
]);
|
||||
// Use getter functions to avoid unnecessary re-renders caused by triggering
|
||||
// all the getters of the returned swr object.
|
||||
return {
|
||||
size: resolvePageSize(),
|
||||
setSize,
|
||||
mutate,
|
||||
get data () {
|
||||
return swr.data;
|
||||
},
|
||||
get error () {
|
||||
return swr.error;
|
||||
},
|
||||
get isValidating () {
|
||||
return swr.isValidating;
|
||||
},
|
||||
get isLoading () {
|
||||
return swr.isLoading;
|
||||
}
|
||||
};
|
||||
};
|
||||
const useSWRInfinite = withMiddleware(useSWR, infinite);
|
||||
|
||||
export { useSWRInfinite as default, infinite, unstable_serialize };
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { s as serialize } from '../serialize-hqt5osie.mjs';
|
||||
import { INFINITE_PREFIX } from '../_internal/constants.mjs';
|
||||
|
||||
const getFirstPageKey = (getKey)=>{
|
||||
return serialize(getKey ? getKey(0, null) : null)[0];
|
||||
};
|
||||
const unstable_serialize = (getKey)=>{
|
||||
return INFINITE_PREFIX + getFirstPageKey(getKey);
|
||||
};
|
||||
|
||||
export { unstable_serialize };
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
import { K as Key, A as Arguments, c as SWRResponse } from '../types-l2s7dphv.js';
|
||||
|
||||
type FetcherResponse<Data> = Data | Promise<Data>;
|
||||
type FetcherOptions<ExtraArg = unknown> = Readonly<{
|
||||
arg: ExtraArg;
|
||||
}>;
|
||||
type MutationFetcher<Data = unknown, SWRKey extends Key = Key, ExtraArg = unknown> = SWRKey extends () => (infer Arg) | null | undefined | false ? (key: Arg, options: FetcherOptions<ExtraArg>) => FetcherResponse<Data> : SWRKey extends null | undefined | false ? never : SWRKey extends infer Arg ? (key: Arg, options: FetcherOptions<ExtraArg>) => FetcherResponse<Data> : never;
|
||||
type SWRMutationConfiguration<Data, Error, SWRMutationKey extends Key = Key, ExtraArg = any, SWRData = any> = {
|
||||
revalidate?: boolean | ((data: Data, key: Arguments) => boolean);
|
||||
populateCache?: boolean | ((result: Data, currentData: SWRData | undefined) => SWRData);
|
||||
optimisticData?: SWRData | ((currentData?: SWRData) => SWRData);
|
||||
rollbackOnError?: boolean | ((error: unknown) => boolean);
|
||||
fetcher?: MutationFetcher<Data, SWRMutationKey, ExtraArg>;
|
||||
onSuccess?: (data: Data, key: string, config: Readonly<SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData>>) => void;
|
||||
onError?: (err: Error, key: string, config: Readonly<SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData>>) => void;
|
||||
};
|
||||
type RemoveUndefined<T> = T extends undefined ? never : T;
|
||||
type IsUndefinedIncluded<T> = undefined extends T ? true : false;
|
||||
interface TriggerWithArgs<Data = any, Error = any, SWRMutationKey extends Key = Key, ExtraArg = never> {
|
||||
<SWRData = Data>(extraArgument: ExtraArg, options?: SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData>): Promise<Data>;
|
||||
<SWRData = Data>(extraArgument: ExtraArg, options?: SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData> & {
|
||||
throwOnError: true;
|
||||
}): Promise<RemoveUndefined<Data>>;
|
||||
<SWRData = Data>(extraArgument: ExtraArg, options?: SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData> & {
|
||||
throwOnError: false;
|
||||
}): Promise<Data | undefined>;
|
||||
}
|
||||
interface TriggerWithOptionsArgs<Data = any, Error = any, SWRMutationKey extends Key = Key, ExtraArg = never> {
|
||||
<SWRData = Data>(extraArgument?: ExtraArg, options?: SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData>): Promise<Data>;
|
||||
<SWRData = Data>(extraArgument?: ExtraArg, options?: SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData> & {
|
||||
throwOnError: true;
|
||||
}): Promise<RemoveUndefined<Data>>;
|
||||
<SWRData = Data>(extraArgument?: ExtraArg, options?: SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData> & {
|
||||
throwOnError: false;
|
||||
}): Promise<Data | undefined>;
|
||||
}
|
||||
interface TriggerWithoutArgs<Data = any, Error = any, SWRMutationKey extends Key = Key, ExtraArg = never> {
|
||||
<SWRData = Data>(extraArgument?: null, options?: SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData>): Promise<Data>;
|
||||
<SWRData = Data>(extraArgument?: null, options?: SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData> & {
|
||||
throwOnError: true;
|
||||
}): Promise<RemoveUndefined<Data>>;
|
||||
<SWRData = Data>(extraArgument?: null, options?: SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData> & {
|
||||
throwOnError: false;
|
||||
}): Promise<Data>;
|
||||
}
|
||||
interface SWRMutationResponse<Data = any, Error = any, SWRMutationKey extends Key = Key, ExtraArg = never> extends Pick<SWRResponse<Data, Error>, 'data' | 'error'> {
|
||||
/**
|
||||
* Indicates if the mutation is in progress.
|
||||
*/
|
||||
isMutating: boolean;
|
||||
/**
|
||||
* Function to trigger the mutation. You can also pass an extra argument to
|
||||
* the fetcher, and override the options for the mutation hook.
|
||||
*/
|
||||
trigger: [ExtraArg] extends [never] ? TriggerWithoutArgs<Data, Error, SWRMutationKey, ExtraArg> : IsUndefinedIncluded<ExtraArg> extends true ? TriggerWithOptionsArgs<Data, Error, SWRMutationKey, ExtraArg> : TriggerWithArgs<Data, Error, SWRMutationKey, ExtraArg>;
|
||||
/**
|
||||
* Function to reset the mutation state (`data`, `error`, and `isMutating`).
|
||||
*/
|
||||
reset: () => void;
|
||||
}
|
||||
interface SWRMutationHook {
|
||||
<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
|
||||
* conditions for that resource.
|
||||
*/
|
||||
key: SWRMutationKey,
|
||||
/**
|
||||
* The function to trigger the mutation that accepts the key, extra argument
|
||||
* and options. For example:
|
||||
*
|
||||
* ```jsx
|
||||
* (api, data) => fetch(api, {
|
||||
* method: 'POST',
|
||||
* body: JSON.stringify(data)
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
fetcher: MutationFetcher<Data, SWRMutationKey, ExtraArg>,
|
||||
/**
|
||||
* Extra options for the mutation hook.
|
||||
*/
|
||||
options?: SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData> & {
|
||||
throwOnError?: boolean;
|
||||
}): SWRMutationResponse<Data, Error, SWRMutationKey, ExtraArg>;
|
||||
<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
|
||||
* conditions for that resource.
|
||||
*/
|
||||
key: SWRMutationKey,
|
||||
/**
|
||||
* The function to trigger the mutation that accepts the key, extra argument
|
||||
* and options. For example:
|
||||
*
|
||||
* ```jsx
|
||||
* (api, data) => fetch(api, {
|
||||
* method: 'POST',
|
||||
* body: JSON.stringify(data)
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
fetcher: MutationFetcher<Data, SWRMutationKey, ExtraArg>,
|
||||
/**
|
||||
* Extra options for the mutation hook.
|
||||
*/
|
||||
options?: SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData> & {
|
||||
throwOnError: false;
|
||||
}): SWRMutationResponse<Data | undefined, Error, SWRMutationKey, ExtraArg>;
|
||||
<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
|
||||
* conditions for that resource.
|
||||
*/
|
||||
key: SWRMutationKey,
|
||||
/**
|
||||
* The function to trigger the mutation that accepts the key, extra argument
|
||||
* and options. For example:
|
||||
*
|
||||
* ```jsx
|
||||
* (api, data) => fetch(api, {
|
||||
* method: 'POST',
|
||||
* body: JSON.stringify(data)
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
fetcher: MutationFetcher<Data, SWRMutationKey, ExtraArg>,
|
||||
/**
|
||||
* Extra options for the mutation hook.
|
||||
*/
|
||||
options?: SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData> & {
|
||||
throwOnError: true;
|
||||
}): SWRMutationResponse<Data, Error, SWRMutationKey, ExtraArg>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A hook to define and manually trigger remote mutations like POST, PUT, DELETE and PATCH use cases.
|
||||
*
|
||||
* @link https://swr.vercel.app/docs/mutation
|
||||
* @example
|
||||
* ```jsx
|
||||
* import useSWRMutation from 'swr/mutation'
|
||||
*
|
||||
* const {
|
||||
* data,
|
||||
* error,
|
||||
* trigger,
|
||||
* reset,
|
||||
* isMutating
|
||||
* } = useSWRMutation(key, fetcher, options?)
|
||||
* ```
|
||||
*/
|
||||
declare const useSWRMutation: SWRMutationHook;
|
||||
|
||||
export { useSWRMutation as default };
|
||||
export type { MutationFetcher, SWRMutationConfiguration, SWRMutationHook, SWRMutationResponse, TriggerWithArgs, TriggerWithOptionsArgs, TriggerWithoutArgs };
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
import { K as Key, A as Arguments, x as SWRResponse } from '../types-m1fld6v2.js';
|
||||
|
||||
type FetcherResponse<Data> = Data | Promise<Data>;
|
||||
type FetcherOptions<ExtraArg = unknown> = Readonly<{
|
||||
arg: ExtraArg;
|
||||
}>;
|
||||
type MutationFetcher<Data = unknown, SWRKey extends Key = Key, ExtraArg = unknown> = SWRKey extends () => (infer Arg) | null | undefined | false ? (key: Arg, options: FetcherOptions<ExtraArg>) => FetcherResponse<Data> : SWRKey extends null | undefined | false ? never : SWRKey extends infer Arg ? (key: Arg, options: FetcherOptions<ExtraArg>) => FetcherResponse<Data> : never;
|
||||
type SWRMutationConfiguration<Data, Error, SWRMutationKey extends Key = Key, ExtraArg = any, SWRData = any> = {
|
||||
revalidate?: boolean | ((data: Data, key: Arguments) => boolean);
|
||||
populateCache?: boolean | ((result: Data, currentData: SWRData | undefined) => SWRData);
|
||||
optimisticData?: SWRData | ((currentData?: SWRData) => SWRData);
|
||||
rollbackOnError?: boolean | ((error: unknown) => boolean);
|
||||
fetcher?: MutationFetcher<Data, SWRMutationKey, ExtraArg>;
|
||||
onSuccess?: (data: Data, key: string, config: Readonly<SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData>>) => void;
|
||||
onError?: (err: Error, key: string, config: Readonly<SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData>>) => void;
|
||||
};
|
||||
type RemoveUndefined<T> = T extends undefined ? never : T;
|
||||
type IsUndefinedIncluded<T> = undefined extends T ? true : false;
|
||||
interface TriggerWithArgs<Data = any, Error = any, SWRMutationKey extends Key = Key, ExtraArg = never> {
|
||||
<SWRData = Data>(extraArgument: ExtraArg, options?: SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData>): Promise<Data>;
|
||||
<SWRData = Data>(extraArgument: ExtraArg, options?: SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData> & {
|
||||
throwOnError: true;
|
||||
}): Promise<RemoveUndefined<Data>>;
|
||||
<SWRData = Data>(extraArgument: ExtraArg, options?: SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData> & {
|
||||
throwOnError: false;
|
||||
}): Promise<Data | undefined>;
|
||||
}
|
||||
interface TriggerWithOptionsArgs<Data = any, Error = any, SWRMutationKey extends Key = Key, ExtraArg = never> {
|
||||
<SWRData = Data>(extraArgument?: ExtraArg, options?: SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData>): Promise<Data>;
|
||||
<SWRData = Data>(extraArgument?: ExtraArg, options?: SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData> & {
|
||||
throwOnError: true;
|
||||
}): Promise<RemoveUndefined<Data>>;
|
||||
<SWRData = Data>(extraArgument?: ExtraArg, options?: SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData> & {
|
||||
throwOnError: false;
|
||||
}): Promise<Data | undefined>;
|
||||
}
|
||||
interface TriggerWithoutArgs<Data = any, Error = any, SWRMutationKey extends Key = Key, ExtraArg = never> {
|
||||
<SWRData = Data>(extraArgument?: null, options?: SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData>): Promise<Data>;
|
||||
<SWRData = Data>(extraArgument?: null, options?: SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData> & {
|
||||
throwOnError: true;
|
||||
}): Promise<RemoveUndefined<Data>>;
|
||||
<SWRData = Data>(extraArgument?: null, options?: SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData> & {
|
||||
throwOnError: false;
|
||||
}): Promise<Data>;
|
||||
}
|
||||
interface SWRMutationResponse<Data = any, Error = any, SWRMutationKey extends Key = Key, ExtraArg = never> extends Pick<SWRResponse<Data, Error>, 'data' | 'error'> {
|
||||
/**
|
||||
* Indicates if the mutation is in progress.
|
||||
*/
|
||||
isMutating: boolean;
|
||||
/**
|
||||
* Function to trigger the mutation. You can also pass an extra argument to
|
||||
* the fetcher, and override the options for the mutation hook.
|
||||
*/
|
||||
trigger: [ExtraArg] extends [never] ? TriggerWithoutArgs<Data, Error, SWRMutationKey, ExtraArg> : IsUndefinedIncluded<ExtraArg> extends true ? TriggerWithOptionsArgs<Data, Error, SWRMutationKey, ExtraArg> : TriggerWithArgs<Data, Error, SWRMutationKey, ExtraArg>;
|
||||
/**
|
||||
* Function to reset the mutation state (`data`, `error`, and `isMutating`).
|
||||
*/
|
||||
reset: () => void;
|
||||
}
|
||||
interface SWRMutationHook {
|
||||
<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
|
||||
* conditions for that resource.
|
||||
*/
|
||||
key: SWRMutationKey,
|
||||
/**
|
||||
* The function to trigger the mutation that accepts the key, extra argument
|
||||
* and options. For example:
|
||||
*
|
||||
* ```jsx
|
||||
* (api, data) => fetch(api, {
|
||||
* method: 'POST',
|
||||
* body: JSON.stringify(data)
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
fetcher: MutationFetcher<Data, SWRMutationKey, ExtraArg>,
|
||||
/**
|
||||
* Extra options for the mutation hook.
|
||||
*/
|
||||
options?: SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData> & {
|
||||
throwOnError?: boolean;
|
||||
}): SWRMutationResponse<Data, Error, SWRMutationKey, ExtraArg>;
|
||||
<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
|
||||
* conditions for that resource.
|
||||
*/
|
||||
key: SWRMutationKey,
|
||||
/**
|
||||
* The function to trigger the mutation that accepts the key, extra argument
|
||||
* and options. For example:
|
||||
*
|
||||
* ```jsx
|
||||
* (api, data) => fetch(api, {
|
||||
* method: 'POST',
|
||||
* body: JSON.stringify(data)
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
fetcher: MutationFetcher<Data, SWRMutationKey, ExtraArg>,
|
||||
/**
|
||||
* Extra options for the mutation hook.
|
||||
*/
|
||||
options?: SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData> & {
|
||||
throwOnError: false;
|
||||
}): SWRMutationResponse<Data | undefined, Error, SWRMutationKey, ExtraArg>;
|
||||
<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
|
||||
* conditions for that resource.
|
||||
*/
|
||||
key: SWRMutationKey,
|
||||
/**
|
||||
* The function to trigger the mutation that accepts the key, extra argument
|
||||
* and options. For example:
|
||||
*
|
||||
* ```jsx
|
||||
* (api, data) => fetch(api, {
|
||||
* method: 'POST',
|
||||
* body: JSON.stringify(data)
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
fetcher: MutationFetcher<Data, SWRMutationKey, ExtraArg>,
|
||||
/**
|
||||
* Extra options for the mutation hook.
|
||||
*/
|
||||
options?: SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData> & {
|
||||
throwOnError: true;
|
||||
}): SWRMutationResponse<Data, Error, SWRMutationKey, ExtraArg>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A hook to define and manually trigger remote mutations like POST, PUT, DELETE and PATCH use cases.
|
||||
*
|
||||
* @link https://swr.vercel.app/docs/mutation
|
||||
* @example
|
||||
* ```jsx
|
||||
* import useSWRMutation from 'swr/mutation'
|
||||
*
|
||||
* const {
|
||||
* data,
|
||||
* error,
|
||||
* trigger,
|
||||
* reset,
|
||||
* isMutating
|
||||
* } = useSWRMutation(key, fetcher, options?)
|
||||
* ```
|
||||
*/
|
||||
declare const useSWRMutation: SWRMutationHook;
|
||||
|
||||
export { useSWRMutation as default };
|
||||
export type { MutationFetcher, SWRMutationConfiguration, SWRMutationHook, SWRMutationResponse, TriggerWithArgs, TriggerWithOptionsArgs, TriggerWithoutArgs };
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
var React = require('react');
|
||||
var useSwr = require('../use-swr-guqrv3k1.js');
|
||||
var bunchee_group_0 = require('../config-context-s960zqob.js');
|
||||
var _internal_index = require('../_internal/index.js');
|
||||
|
||||
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
||||
|
||||
var React__default = /*#__PURE__*/_interopDefault(React);
|
||||
|
||||
const startTransition = bunchee_group_0.IS_REACT_LEGACY ? (cb)=>{
|
||||
cb();
|
||||
} : React__default.default.startTransition;
|
||||
/**
|
||||
* An implementation of state with dependency-tracking.
|
||||
* @param initialState - The initial state object.
|
||||
*/ const useStateWithDeps = (initialState)=>{
|
||||
const [, rerender] = React.useState({});
|
||||
const unmountedRef = React.useRef(false);
|
||||
const stateRef = React.useRef(initialState);
|
||||
// If a state property (data, error, or isValidating) is accessed by the render
|
||||
// function, we mark the property as a dependency so if it is updated again
|
||||
// in the future, we trigger a rerender.
|
||||
// This is also known as dependency-tracking.
|
||||
const stateDependenciesRef = React.useRef({
|
||||
data: false,
|
||||
error: false,
|
||||
isValidating: false
|
||||
});
|
||||
/**
|
||||
* Updates state and triggers re-render if necessary.
|
||||
* @param payload To change stateRef, pass the values explicitly to setState:
|
||||
* @example
|
||||
* ```js
|
||||
* setState({
|
||||
* isValidating: false
|
||||
* data: newData // set data to newData
|
||||
* error: undefined // set error to undefined
|
||||
* })
|
||||
*
|
||||
* setState({
|
||||
* isValidating: false
|
||||
* data: undefined // set data to undefined
|
||||
* error: err // set error to err
|
||||
* })
|
||||
* ```
|
||||
*/ const setState = React.useCallback((payload)=>{
|
||||
let shouldRerender = false;
|
||||
const currentState = stateRef.current;
|
||||
for(const key in payload){
|
||||
if (Object.prototype.hasOwnProperty.call(payload, key)) {
|
||||
const k = key;
|
||||
// If the property has changed, update the state and mark rerender as
|
||||
// needed.
|
||||
if (currentState[k] !== payload[k]) {
|
||||
currentState[k] = payload[k];
|
||||
// If the property is accessed by the component, a rerender should be
|
||||
// triggered.
|
||||
if (stateDependenciesRef.current[k]) {
|
||||
shouldRerender = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (shouldRerender && !unmountedRef.current) {
|
||||
rerender({});
|
||||
}
|
||||
}, []);
|
||||
bunchee_group_0.useIsomorphicLayoutEffect(()=>{
|
||||
unmountedRef.current = false;
|
||||
return ()=>{
|
||||
unmountedRef.current = true;
|
||||
};
|
||||
});
|
||||
return [
|
||||
stateRef,
|
||||
stateDependenciesRef.current,
|
||||
setState
|
||||
];
|
||||
};
|
||||
|
||||
const mutation = ()=>(key, fetcher, config = {})=>{
|
||||
const { mutate } = _internal_index.useSWRConfig();
|
||||
const keyRef = React.useRef(key);
|
||||
const fetcherRef = React.useRef(fetcher);
|
||||
const configRef = React.useRef(config);
|
||||
// Ditch all mutation results that happened earlier than this timestamp.
|
||||
const ditchMutationsUntilRef = React.useRef(0);
|
||||
const [stateRef, stateDependencies, setState] = useStateWithDeps({
|
||||
data: bunchee_group_0.UNDEFINED,
|
||||
error: bunchee_group_0.UNDEFINED,
|
||||
isMutating: false
|
||||
});
|
||||
const currentState = stateRef.current;
|
||||
const trigger = React.useCallback(async (arg, opts)=>{
|
||||
const [serializedKey, resolvedKey] = bunchee_group_0.serialize(keyRef.current);
|
||||
if (!fetcherRef.current) {
|
||||
throw new Error('Can’t trigger the mutation: missing fetcher.');
|
||||
}
|
||||
if (!serializedKey) {
|
||||
throw new Error('Can’t trigger the mutation: missing key.');
|
||||
}
|
||||
// Disable cache population by default.
|
||||
const options = bunchee_group_0.mergeObjects(bunchee_group_0.mergeObjects({
|
||||
populateCache: false,
|
||||
throwOnError: true
|
||||
}, configRef.current), opts);
|
||||
// Trigger a mutation, and also track the timestamp. Any mutation that happened
|
||||
// earlier this timestamp should be ignored.
|
||||
const mutationStartedAt = bunchee_group_0.getTimestamp();
|
||||
ditchMutationsUntilRef.current = mutationStartedAt;
|
||||
setState({
|
||||
isMutating: true
|
||||
});
|
||||
try {
|
||||
const data = await mutate(serializedKey, fetcherRef.current(resolvedKey, {
|
||||
arg
|
||||
}), // We must throw the error here so we can catch and update the states.
|
||||
bunchee_group_0.mergeObjects(options, {
|
||||
throwOnError: true
|
||||
}));
|
||||
// If it's reset after the mutation, we don't broadcast any state change.
|
||||
if (ditchMutationsUntilRef.current <= mutationStartedAt) {
|
||||
startTransition(()=>setState({
|
||||
data,
|
||||
isMutating: false,
|
||||
error: undefined
|
||||
}));
|
||||
options.onSuccess == null ? void 0 : options.onSuccess.call(options, data, serializedKey, options);
|
||||
}
|
||||
return data;
|
||||
} catch (error) {
|
||||
// If it's reset after the mutation, we don't broadcast any state change
|
||||
// or throw because it's discarded.
|
||||
if (ditchMutationsUntilRef.current <= mutationStartedAt) {
|
||||
startTransition(()=>setState({
|
||||
error: error,
|
||||
isMutating: false
|
||||
}));
|
||||
options.onError == null ? void 0 : options.onError.call(options, error, serializedKey, options);
|
||||
if (options.throwOnError) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, // eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[]);
|
||||
const reset = React.useCallback(()=>{
|
||||
ditchMutationsUntilRef.current = bunchee_group_0.getTimestamp();
|
||||
setState({
|
||||
data: bunchee_group_0.UNDEFINED,
|
||||
error: bunchee_group_0.UNDEFINED,
|
||||
isMutating: false
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
bunchee_group_0.useIsomorphicLayoutEffect(()=>{
|
||||
keyRef.current = key;
|
||||
fetcherRef.current = fetcher;
|
||||
configRef.current = config;
|
||||
});
|
||||
// We don't return `mutate` here as it can be pretty confusing (e.g. people
|
||||
// calling `mutate` but they actually mean `trigger`).
|
||||
// And also, `mutate` relies on the useSWR hook to exist too.
|
||||
return {
|
||||
trigger,
|
||||
reset,
|
||||
get data () {
|
||||
stateDependencies.data = true;
|
||||
return currentState.data;
|
||||
},
|
||||
get error () {
|
||||
stateDependencies.error = true;
|
||||
return currentState.error;
|
||||
},
|
||||
get isMutating () {
|
||||
stateDependencies.isMutating = true;
|
||||
return currentState.isMutating;
|
||||
}
|
||||
};
|
||||
};
|
||||
/**
|
||||
* A hook to define and manually trigger remote mutations like POST, PUT, DELETE and PATCH use cases.
|
||||
*
|
||||
* @link https://swr.vercel.app/docs/mutation
|
||||
* @example
|
||||
* ```jsx
|
||||
* import useSWRMutation from 'swr/mutation'
|
||||
*
|
||||
* const {
|
||||
* data,
|
||||
* error,
|
||||
* trigger,
|
||||
* reset,
|
||||
* isMutating
|
||||
* } = useSWRMutation(key, fetcher, options?)
|
||||
* ```
|
||||
*/ const useSWRMutation = _internal_index.withMiddleware(useSwr.useSWR, mutation);
|
||||
|
||||
exports.default = useSWRMutation;
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
import React, { useState, useRef, useCallback } from 'react';
|
||||
import { u as useSWR } from '../use-swr-emll9s78.mjs';
|
||||
import { j as IS_REACT_LEGACY, u as useIsomorphicLayoutEffect, s as serialize, m as mergeObjects, g as getTimestamp, U as UNDEFINED } from '../config-context-ext53wcz.mjs';
|
||||
import { withMiddleware, useSWRConfig } from '../_internal/index.mjs';
|
||||
|
||||
const startTransition = IS_REACT_LEGACY ? (cb)=>{
|
||||
cb();
|
||||
} : React.startTransition;
|
||||
/**
|
||||
* An implementation of state with dependency-tracking.
|
||||
* @param initialState - The initial state object.
|
||||
*/ const useStateWithDeps = (initialState)=>{
|
||||
const [, rerender] = useState({});
|
||||
const unmountedRef = useRef(false);
|
||||
const stateRef = useRef(initialState);
|
||||
// If a state property (data, error, or isValidating) is accessed by the render
|
||||
// function, we mark the property as a dependency so if it is updated again
|
||||
// in the future, we trigger a rerender.
|
||||
// This is also known as dependency-tracking.
|
||||
const stateDependenciesRef = useRef({
|
||||
data: false,
|
||||
error: false,
|
||||
isValidating: false
|
||||
});
|
||||
/**
|
||||
* Updates state and triggers re-render if necessary.
|
||||
* @param payload To change stateRef, pass the values explicitly to setState:
|
||||
* @example
|
||||
* ```js
|
||||
* setState({
|
||||
* isValidating: false
|
||||
* data: newData // set data to newData
|
||||
* error: undefined // set error to undefined
|
||||
* })
|
||||
*
|
||||
* setState({
|
||||
* isValidating: false
|
||||
* data: undefined // set data to undefined
|
||||
* error: err // set error to err
|
||||
* })
|
||||
* ```
|
||||
*/ const setState = useCallback((payload)=>{
|
||||
let shouldRerender = false;
|
||||
const currentState = stateRef.current;
|
||||
for(const key in payload){
|
||||
if (Object.prototype.hasOwnProperty.call(payload, key)) {
|
||||
const k = key;
|
||||
// If the property has changed, update the state and mark rerender as
|
||||
// needed.
|
||||
if (currentState[k] !== payload[k]) {
|
||||
currentState[k] = payload[k];
|
||||
// If the property is accessed by the component, a rerender should be
|
||||
// triggered.
|
||||
if (stateDependenciesRef.current[k]) {
|
||||
shouldRerender = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (shouldRerender && !unmountedRef.current) {
|
||||
rerender({});
|
||||
}
|
||||
}, []);
|
||||
useIsomorphicLayoutEffect(()=>{
|
||||
unmountedRef.current = false;
|
||||
return ()=>{
|
||||
unmountedRef.current = true;
|
||||
};
|
||||
});
|
||||
return [
|
||||
stateRef,
|
||||
stateDependenciesRef.current,
|
||||
setState
|
||||
];
|
||||
};
|
||||
|
||||
const mutation = ()=>(key, fetcher, config = {})=>{
|
||||
const { mutate } = useSWRConfig();
|
||||
const keyRef = useRef(key);
|
||||
const fetcherRef = useRef(fetcher);
|
||||
const configRef = useRef(config);
|
||||
// Ditch all mutation results that happened earlier than this timestamp.
|
||||
const ditchMutationsUntilRef = useRef(0);
|
||||
const [stateRef, stateDependencies, setState] = useStateWithDeps({
|
||||
data: UNDEFINED,
|
||||
error: UNDEFINED,
|
||||
isMutating: false
|
||||
});
|
||||
const currentState = stateRef.current;
|
||||
const trigger = useCallback(async (arg, opts)=>{
|
||||
const [serializedKey, resolvedKey] = serialize(keyRef.current);
|
||||
if (!fetcherRef.current) {
|
||||
throw new Error('Can’t trigger the mutation: missing fetcher.');
|
||||
}
|
||||
if (!serializedKey) {
|
||||
throw new Error('Can’t trigger the mutation: missing key.');
|
||||
}
|
||||
// Disable cache population by default.
|
||||
const options = mergeObjects(mergeObjects({
|
||||
populateCache: false,
|
||||
throwOnError: true
|
||||
}, configRef.current), opts);
|
||||
// Trigger a mutation, and also track the timestamp. Any mutation that happened
|
||||
// earlier this timestamp should be ignored.
|
||||
const mutationStartedAt = getTimestamp();
|
||||
ditchMutationsUntilRef.current = mutationStartedAt;
|
||||
setState({
|
||||
isMutating: true
|
||||
});
|
||||
try {
|
||||
const data = await mutate(serializedKey, fetcherRef.current(resolvedKey, {
|
||||
arg
|
||||
}), // We must throw the error here so we can catch and update the states.
|
||||
mergeObjects(options, {
|
||||
throwOnError: true
|
||||
}));
|
||||
// If it's reset after the mutation, we don't broadcast any state change.
|
||||
if (ditchMutationsUntilRef.current <= mutationStartedAt) {
|
||||
startTransition(()=>setState({
|
||||
data,
|
||||
isMutating: false,
|
||||
error: undefined
|
||||
}));
|
||||
options.onSuccess == null ? void 0 : options.onSuccess.call(options, data, serializedKey, options);
|
||||
}
|
||||
return data;
|
||||
} catch (error) {
|
||||
// If it's reset after the mutation, we don't broadcast any state change
|
||||
// or throw because it's discarded.
|
||||
if (ditchMutationsUntilRef.current <= mutationStartedAt) {
|
||||
startTransition(()=>setState({
|
||||
error: error,
|
||||
isMutating: false
|
||||
}));
|
||||
options.onError == null ? void 0 : options.onError.call(options, error, serializedKey, options);
|
||||
if (options.throwOnError) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, // eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[]);
|
||||
const reset = useCallback(()=>{
|
||||
ditchMutationsUntilRef.current = getTimestamp();
|
||||
setState({
|
||||
data: UNDEFINED,
|
||||
error: UNDEFINED,
|
||||
isMutating: false
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
useIsomorphicLayoutEffect(()=>{
|
||||
keyRef.current = key;
|
||||
fetcherRef.current = fetcher;
|
||||
configRef.current = config;
|
||||
});
|
||||
// We don't return `mutate` here as it can be pretty confusing (e.g. people
|
||||
// calling `mutate` but they actually mean `trigger`).
|
||||
// And also, `mutate` relies on the useSWR hook to exist too.
|
||||
return {
|
||||
trigger,
|
||||
reset,
|
||||
get data () {
|
||||
stateDependencies.data = true;
|
||||
return currentState.data;
|
||||
},
|
||||
get error () {
|
||||
stateDependencies.error = true;
|
||||
return currentState.error;
|
||||
},
|
||||
get isMutating () {
|
||||
stateDependencies.isMutating = true;
|
||||
return currentState.isMutating;
|
||||
}
|
||||
};
|
||||
};
|
||||
/**
|
||||
* A hook to define and manually trigger remote mutations like POST, PUT, DELETE and PATCH use cases.
|
||||
*
|
||||
* @link https://swr.vercel.app/docs/mutation
|
||||
* @example
|
||||
* ```jsx
|
||||
* import useSWRMutation from 'swr/mutation'
|
||||
*
|
||||
* const {
|
||||
* data,
|
||||
* error,
|
||||
* trigger,
|
||||
* reset,
|
||||
* isMutating
|
||||
* } = useSWRMutation(key, fetcher, options?)
|
||||
* ```
|
||||
*/ const useSWRMutation = withMiddleware(useSWR, mutation);
|
||||
|
||||
export { useSWRMutation as default };
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import { K as Key, A as Arguments } from './types-l2s7dphv.js';
|
||||
|
||||
declare const serialize: (key: Key) => [string, Arguments];
|
||||
|
||||
export { serialize as s };
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import { K as Key, A as Arguments } from './types-m1fld6v2.js';
|
||||
|
||||
declare const serialize: (key: Key) => [string, Arguments];
|
||||
|
||||
export { serialize as s };
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
// Shared state between server components and client components
|
||||
const noop = ()=>{};
|
||||
// Using noop() as the undefined value as undefined can be replaced
|
||||
// by something else. Prettier ignore and extra parentheses are necessary here
|
||||
// to ensure that tsc doesn't remove the __NOINLINE__ comment.
|
||||
// prettier-ignore
|
||||
const UNDEFINED = /*#__NOINLINE__*/ noop();
|
||||
const OBJECT = Object;
|
||||
const isUndefined = (v)=>v === UNDEFINED;
|
||||
const isFunction = (v)=>typeof v == 'function';
|
||||
|
||||
// use WeakMap to store the object->key mapping
|
||||
// so the objects can be garbage collected.
|
||||
// WeakMap uses a hashtable under the hood, so the lookup
|
||||
// complexity is almost O(1).
|
||||
const table = new WeakMap();
|
||||
const getTypeName = (value)=>OBJECT.prototype.toString.call(value);
|
||||
const isObjectTypeName = (typeName, type)=>typeName === `[object ${type}]`;
|
||||
// counter of the key
|
||||
let counter = 0;
|
||||
// A stable hash implementation that supports:
|
||||
// - Fast and ensures unique hash properties
|
||||
// - Handles unserializable values
|
||||
// - Handles object key ordering
|
||||
// - Generates short results
|
||||
//
|
||||
// This is not a serialization function, and the result is not guaranteed to be
|
||||
// parsable.
|
||||
const stableHash = (arg)=>{
|
||||
const type = typeof arg;
|
||||
const typeName = getTypeName(arg);
|
||||
const isDate = isObjectTypeName(typeName, 'Date');
|
||||
const isRegex = isObjectTypeName(typeName, 'RegExp');
|
||||
const isPlainObject = isObjectTypeName(typeName, 'Object');
|
||||
let result;
|
||||
let index;
|
||||
if (OBJECT(arg) === arg && !isDate && !isRegex) {
|
||||
// Object/function, not null/date/regexp. Use WeakMap to store the id first.
|
||||
// If it's already hashed, directly return the result.
|
||||
result = table.get(arg);
|
||||
if (result) return result;
|
||||
// Store the hash first for circular reference detection before entering the
|
||||
// recursive `stableHash` calls.
|
||||
// For other objects like set and map, we use this id directly as the hash.
|
||||
result = ++counter + '~';
|
||||
table.set(arg, result);
|
||||
if (Array.isArray(arg)) {
|
||||
// Array.
|
||||
result = '@';
|
||||
for(index = 0; index < arg.length; index++){
|
||||
result += stableHash(arg[index]) + ',';
|
||||
}
|
||||
table.set(arg, result);
|
||||
}
|
||||
if (isPlainObject) {
|
||||
// Object, sort keys.
|
||||
result = '#';
|
||||
const keys = OBJECT.keys(arg).sort();
|
||||
while(!isUndefined(index = keys.pop())){
|
||||
if (!isUndefined(arg[index])) {
|
||||
result += index + ':' + stableHash(arg[index]) + ',';
|
||||
}
|
||||
}
|
||||
table.set(arg, result);
|
||||
}
|
||||
} else {
|
||||
result = isDate ? arg.toJSON() : type == 'symbol' ? arg.toString() : type == 'string' ? JSON.stringify(arg) : '' + arg;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const serialize = (key)=>{
|
||||
if (isFunction(key)) {
|
||||
try {
|
||||
key = key();
|
||||
} catch (err) {
|
||||
// dependencies not ready
|
||||
key = '';
|
||||
}
|
||||
}
|
||||
// Use the original key as the argument of fetcher. This can be a string or an
|
||||
// array of values.
|
||||
const args = key;
|
||||
// If key is not falsy, or not an empty array, hash it.
|
||||
key = typeof key == 'string' ? key : (Array.isArray(key) ? key.length : key) ? stableHash(key) : '';
|
||||
return [
|
||||
key,
|
||||
args
|
||||
];
|
||||
};
|
||||
|
||||
export { serialize as s };
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import { K as Key, B as BareFetcher, F as FetcherResponse, C as CacheData } from './types-l2s7dphv.js';
|
||||
|
||||
type PreloadFetcher<Data = unknown, SWRKey extends Key = Key> = SWRKey extends () => infer Arg ? (arg: Arg) => FetcherResponse<Data> : SWRKey extends infer Arg ? (arg: Arg) => FetcherResponse<Data> : never;
|
||||
declare const preload: <Data = any, SWRKey extends Key = Key, Fetcher extends BareFetcher = PreloadFetcher<Data, SWRKey>>(key_: SWRKey, fetcher: Fetcher) => CacheData<Data>;
|
||||
|
||||
export { preload as p };
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import { s as serialize } from './serialize-hqt5osie.mjs';
|
||||
|
||||
const preload = (key_, fetcher)=>{
|
||||
const [key, fnArg] = serialize(key_);
|
||||
return {
|
||||
[key]: fetcher(fnArg)
|
||||
};
|
||||
};
|
||||
|
||||
export { preload as p };
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
import { K as Key, d as MutatorCallback, e as SWRConfiguration, M as Middleware } from '../types-l2s7dphv.js';
|
||||
|
||||
type SWRSubscriptionOptions<Data = any, Error = any> = {
|
||||
next: (err?: Error | null, data?: Data | MutatorCallback<Data>) => void;
|
||||
};
|
||||
type SWRSubscription<SWRSubKey extends Key = Key, Data = any, Error = any> = SWRSubKey extends () => (infer Arg) | null | undefined | false ? (key: Arg, { next }: SWRSubscriptionOptions<Data, Error>) => void : SWRSubKey extends null | undefined | false ? never : SWRSubKey extends infer Arg ? (key: Arg, { next }: SWRSubscriptionOptions<Data, Error>) => void : never;
|
||||
type SWRSubscriptionResponse<Data = any, Error = any> = {
|
||||
data?: Data;
|
||||
error?: Error;
|
||||
};
|
||||
type SWRSubscriptionHook = <Data = any, Error = any, SWRSubKey extends Key = Key>(key: SWRSubKey, subscribe: SWRSubscription<SWRSubKey, Data, Error>, config?: SWRConfiguration) => SWRSubscriptionResponse<Data, Error>;
|
||||
|
||||
declare const subscription: Middleware;
|
||||
/**
|
||||
* A hook to subscribe a SWR resource to an external data source for continuous updates.
|
||||
* @experimental This API is experimental and might change in the future.
|
||||
* @example
|
||||
* ```jsx
|
||||
* import useSWRSubscription from 'swr/subscription'
|
||||
*
|
||||
* const { data, error } = useSWRSubscription(key, (key, { next }) => {
|
||||
* const unsubscribe = dataSource.subscribe(key, (err, data) => {
|
||||
* next(err, data)
|
||||
* })
|
||||
* return unsubscribe
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
declare const useSWRSubscription: SWRSubscriptionHook;
|
||||
|
||||
export { useSWRSubscription as default, subscription };
|
||||
export type { SWRSubscription, SWRSubscriptionHook, SWRSubscriptionOptions, SWRSubscriptionResponse };
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
import { K as Key, M as MutatorCallback, g as SWRConfiguration, i as Middleware } from '../types-m1fld6v2.js';
|
||||
|
||||
type SWRSubscriptionOptions<Data = any, Error = any> = {
|
||||
next: (err?: Error | null, data?: Data | MutatorCallback<Data>) => void;
|
||||
};
|
||||
type SWRSubscription<SWRSubKey extends Key = Key, Data = any, Error = any> = SWRSubKey extends () => (infer Arg) | null | undefined | false ? (key: Arg, { next }: SWRSubscriptionOptions<Data, Error>) => void : SWRSubKey extends null | undefined | false ? never : SWRSubKey extends infer Arg ? (key: Arg, { next }: SWRSubscriptionOptions<Data, Error>) => void : never;
|
||||
type SWRSubscriptionResponse<Data = any, Error = any> = {
|
||||
data?: Data;
|
||||
error?: Error;
|
||||
};
|
||||
type SWRSubscriptionHook = <Data = any, Error = any, SWRSubKey extends Key = Key>(key: SWRSubKey, subscribe: SWRSubscription<SWRSubKey, Data, Error>, config?: SWRConfiguration) => SWRSubscriptionResponse<Data, Error>;
|
||||
|
||||
declare const subscription: Middleware;
|
||||
/**
|
||||
* A hook to subscribe a SWR resource to an external data source for continuous updates.
|
||||
* @experimental This API is experimental and might change in the future.
|
||||
* @example
|
||||
* ```jsx
|
||||
* import useSWRSubscription from 'swr/subscription'
|
||||
*
|
||||
* const { data, error } = useSWRSubscription(key, (key, { next }) => {
|
||||
* const unsubscribe = dataSource.subscribe(key, (err, data) => {
|
||||
* next(err, data)
|
||||
* })
|
||||
* return unsubscribe
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
declare const useSWRSubscription: SWRSubscriptionHook;
|
||||
|
||||
export { useSWRSubscription as default, subscription };
|
||||
export type { SWRSubscription, SWRSubscriptionHook, SWRSubscriptionOptions, SWRSubscriptionResponse };
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
var useSwr = require('../use-swr-guqrv3k1.js');
|
||||
var bunchee_group_0 = require('../config-context-s960zqob.js');
|
||||
var _internal_index = require('../_internal/index.js');
|
||||
|
||||
const subscriptionStorage = new WeakMap();
|
||||
const SUBSCRIPTION_PREFIX = '$sub$';
|
||||
const subscription = (useSWRNext)=>(_key, subscribe, config)=>{
|
||||
const [key, args] = bunchee_group_0.serialize(_key);
|
||||
// Prefix the key to avoid conflicts with other SWR resources.
|
||||
const subscriptionKey = key ? SUBSCRIPTION_PREFIX + key : undefined;
|
||||
const swr = useSWRNext(subscriptionKey, null, config);
|
||||
const { cache } = config;
|
||||
// Ensure that the subscription state is scoped by the cache boundary, so
|
||||
// you can have multiple SWR zones with subscriptions having the same key.
|
||||
if (!subscriptionStorage.has(cache)) {
|
||||
subscriptionStorage.set(cache, [
|
||||
new Map(),
|
||||
new Map()
|
||||
]);
|
||||
}
|
||||
const [subscriptions, disposers] = subscriptionStorage.get(cache);
|
||||
bunchee_group_0.useIsomorphicLayoutEffect(()=>{
|
||||
if (!subscriptionKey) return;
|
||||
const [, set] = bunchee_group_0.createCacheHelper(cache, subscriptionKey);
|
||||
const refCount = subscriptions.get(subscriptionKey) || 0;
|
||||
const next = (error, data)=>{
|
||||
if (error !== null && typeof error !== 'undefined') {
|
||||
set({
|
||||
error
|
||||
});
|
||||
} else {
|
||||
set({
|
||||
error: undefined
|
||||
});
|
||||
swr.mutate(data, false);
|
||||
}
|
||||
};
|
||||
// Increment the ref count.
|
||||
subscriptions.set(subscriptionKey, refCount + 1);
|
||||
if (!refCount) {
|
||||
const dispose = subscribe(args, {
|
||||
next
|
||||
});
|
||||
if (typeof dispose !== 'function') {
|
||||
throw new Error('The `subscribe` function must return a function to unsubscribe.');
|
||||
}
|
||||
disposers.set(subscriptionKey, dispose);
|
||||
}
|
||||
return ()=>{
|
||||
const count = subscriptions.get(subscriptionKey) - 1;
|
||||
subscriptions.set(subscriptionKey, count);
|
||||
// Dispose if it's the last one.
|
||||
if (!count) {
|
||||
const dispose = disposers.get(subscriptionKey);
|
||||
dispose == null ? void 0 : dispose();
|
||||
// Remove both entries, so the internal states only retain the
|
||||
// subscription keys that currently have active subscribers.
|
||||
subscriptions.delete(subscriptionKey);
|
||||
disposers.delete(subscriptionKey);
|
||||
}
|
||||
};
|
||||
}, [
|
||||
subscriptionKey
|
||||
]);
|
||||
return {
|
||||
get data () {
|
||||
return swr.data;
|
||||
},
|
||||
get error () {
|
||||
return swr.error;
|
||||
}
|
||||
};
|
||||
};
|
||||
/**
|
||||
* A hook to subscribe a SWR resource to an external data source for continuous updates.
|
||||
* @experimental This API is experimental and might change in the future.
|
||||
* @example
|
||||
* ```jsx
|
||||
* import useSWRSubscription from 'swr/subscription'
|
||||
*
|
||||
* const { data, error } = useSWRSubscription(key, (key, { next }) => {
|
||||
* const unsubscribe = dataSource.subscribe(key, (err, data) => {
|
||||
* next(err, data)
|
||||
* })
|
||||
* return unsubscribe
|
||||
* })
|
||||
* ```
|
||||
*/ const useSWRSubscription = _internal_index.withMiddleware(useSwr.useSWR, subscription);
|
||||
|
||||
exports.default = useSWRSubscription;
|
||||
exports.subscription = subscription;
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
import { u as useSWR } from '../use-swr-emll9s78.mjs';
|
||||
import { s as serialize, u as useIsomorphicLayoutEffect, c as createCacheHelper } from '../config-context-ext53wcz.mjs';
|
||||
import { withMiddleware } from '../_internal/index.mjs';
|
||||
|
||||
const subscriptionStorage = new WeakMap();
|
||||
const SUBSCRIPTION_PREFIX = '$sub$';
|
||||
const subscription = (useSWRNext)=>(_key, subscribe, config)=>{
|
||||
const [key, args] = serialize(_key);
|
||||
// Prefix the key to avoid conflicts with other SWR resources.
|
||||
const subscriptionKey = key ? SUBSCRIPTION_PREFIX + key : undefined;
|
||||
const swr = useSWRNext(subscriptionKey, null, config);
|
||||
const { cache } = config;
|
||||
// Ensure that the subscription state is scoped by the cache boundary, so
|
||||
// you can have multiple SWR zones with subscriptions having the same key.
|
||||
if (!subscriptionStorage.has(cache)) {
|
||||
subscriptionStorage.set(cache, [
|
||||
new Map(),
|
||||
new Map()
|
||||
]);
|
||||
}
|
||||
const [subscriptions, disposers] = subscriptionStorage.get(cache);
|
||||
useIsomorphicLayoutEffect(()=>{
|
||||
if (!subscriptionKey) return;
|
||||
const [, set] = createCacheHelper(cache, subscriptionKey);
|
||||
const refCount = subscriptions.get(subscriptionKey) || 0;
|
||||
const next = (error, data)=>{
|
||||
if (error !== null && typeof error !== 'undefined') {
|
||||
set({
|
||||
error
|
||||
});
|
||||
} else {
|
||||
set({
|
||||
error: undefined
|
||||
});
|
||||
swr.mutate(data, false);
|
||||
}
|
||||
};
|
||||
// Increment the ref count.
|
||||
subscriptions.set(subscriptionKey, refCount + 1);
|
||||
if (!refCount) {
|
||||
const dispose = subscribe(args, {
|
||||
next
|
||||
});
|
||||
if (typeof dispose !== 'function') {
|
||||
throw new Error('The `subscribe` function must return a function to unsubscribe.');
|
||||
}
|
||||
disposers.set(subscriptionKey, dispose);
|
||||
}
|
||||
return ()=>{
|
||||
const count = subscriptions.get(subscriptionKey) - 1;
|
||||
subscriptions.set(subscriptionKey, count);
|
||||
// Dispose if it's the last one.
|
||||
if (!count) {
|
||||
const dispose = disposers.get(subscriptionKey);
|
||||
dispose == null ? void 0 : dispose();
|
||||
// Remove both entries, so the internal states only retain the
|
||||
// subscription keys that currently have active subscribers.
|
||||
subscriptions.delete(subscriptionKey);
|
||||
disposers.delete(subscriptionKey);
|
||||
}
|
||||
};
|
||||
}, [
|
||||
subscriptionKey
|
||||
]);
|
||||
return {
|
||||
get data () {
|
||||
return swr.data;
|
||||
},
|
||||
get error () {
|
||||
return swr.error;
|
||||
}
|
||||
};
|
||||
};
|
||||
/**
|
||||
* A hook to subscribe a SWR resource to an external data source for continuous updates.
|
||||
* @experimental This API is experimental and might change in the future.
|
||||
* @example
|
||||
* ```jsx
|
||||
* import useSWRSubscription from 'swr/subscription'
|
||||
*
|
||||
* const { data, error } = useSWRSubscription(key, (key, { next }) => {
|
||||
* const unsubscribe = dataSource.subscribe(key, (err, data) => {
|
||||
* next(err, data)
|
||||
* })
|
||||
* return unsubscribe
|
||||
* })
|
||||
* ```
|
||||
*/ const useSWRSubscription = withMiddleware(useSWR, subscription);
|
||||
|
||||
export { useSWRSubscription as default, subscription };
|
||||
+971
@@ -0,0 +1,971 @@
|
||||
import { FC, PropsWithChildren } from 'react';
|
||||
import { F as FOCUS_EVENT, R as RECONNECT_EVENT, M as MUTATE_EVENT, E as ERROR_REVALIDATE_EVENT, U as UNLOAD_EVENT } from './events-ny18dyc2.js';
|
||||
import { dequal } from 'dequal/lite';
|
||||
|
||||
declare const SWRConfig$1: FC<PropsWithChildren<{
|
||||
value?: SWRConfigValue | ((parentConfig?: SWRConfigValue) => SWRConfigValue);
|
||||
}>>;
|
||||
|
||||
declare const compare: typeof dequal;
|
||||
declare const cache: Cache<any>;
|
||||
declare const mutate: ScopedMutator;
|
||||
declare const unload: Unloader;
|
||||
|
||||
declare const defaultConfig: FullConfiguration;
|
||||
|
||||
declare const useSWRConfig: () => FullConfiguration;
|
||||
|
||||
type PreloadFetcher<Data = unknown, SWRKey extends Key = Key> = SWRKey extends () => infer Arg ? (arg: Arg) => FetcherResponse<Data> : SWRKey extends infer Arg ? (arg: Arg) => FetcherResponse<Data> : never;
|
||||
declare const preload: <Data = any, SWRKey extends Key = Key, Fetcher extends BareFetcher = PreloadFetcher<Data, SWRKey>>(key_: SWRKey, fetcher: Fetcher) => ReturnType<Fetcher>;
|
||||
|
||||
declare const unstable_serialize: (key: Key) => string;
|
||||
|
||||
declare const SWRConfig: typeof SWRConfig$1 & {
|
||||
defaultValue: FullConfiguration;
|
||||
};
|
||||
|
||||
/**
|
||||
* A hook to fetch data.
|
||||
*
|
||||
* @see {@link https://swr.vercel.app}
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
* import useSWR from 'swr'
|
||||
* function Profile() {
|
||||
* const { data, error, isLoading } = useSWR('/api/user', fetcher)
|
||||
* if (error) return <div>failed to load</div>
|
||||
* if (isLoading) return <div>loading...</div>
|
||||
* return <div>hello {data.name}!</div>
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
declare const useSWR: SWRHook;
|
||||
|
||||
interface SWRGlobalConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Global state tuple containing SWR's internal state management structures.
|
||||
*
|
||||
* This is the core state structure that manages all SWR operations internally.
|
||||
* Each element serves a specific purpose in the SWR ecosystem.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
type GlobalState = [
|
||||
/** Event revalidators: Maps cache keys to arrays of revalidation callbacks */
|
||||
Record<string, RevalidateCallback[]>,
|
||||
/** Mutation timestamps: Maps cache keys to [start_timestamp, end_timestamp] tuples */
|
||||
Record<string, [number, number]>,
|
||||
/** Fetch cache: Maps cache keys to [data, timestamp] tuples */
|
||||
Record<string, [any, number]>,
|
||||
/** Preload cache: Maps cache keys to fetcher responses */
|
||||
Record<string, FetcherResponse<any>>,
|
||||
/** Scoped mutator function for cache updates */
|
||||
ScopedMutator,
|
||||
/** Cache setter function with prev/current value comparison */
|
||||
(key: string, value: any, prev: any) => void,
|
||||
/** Cache subscriber function that returns an unsubscribe function */
|
||||
(key: string, callback: (current: any, prev: any) => void) => () => void,
|
||||
/** Unloader function that clears the cache and notifies subscribers */
|
||||
Unloader,
|
||||
/** Unload generation, bumped on every unload to discard in-flight writes */
|
||||
number
|
||||
];
|
||||
/**
|
||||
* Response type that can be returned by fetcher functions.
|
||||
*
|
||||
* @template Data - The type of data returned by the fetcher
|
||||
* @public
|
||||
*/
|
||||
type FetcherResponse<Data = unknown> = Data | Promise<Data>;
|
||||
/**
|
||||
* Cache data produced on the server and consumed by SWRConfig on the client.
|
||||
*
|
||||
* @experimental
|
||||
* @public
|
||||
*/
|
||||
type CacheData<Data = any> = {
|
||||
[key: string]: FetcherResponse<Data>;
|
||||
};
|
||||
/**
|
||||
* Basic fetcher function that accepts any arguments and returns data or a promise.
|
||||
*
|
||||
* This is the most permissive fetcher type, allowing any number of arguments
|
||||
* of any type. Used when type safety is not required or when dealing with
|
||||
* dynamic fetcher signatures.
|
||||
*
|
||||
* @template Data - The type of data returned by the fetcher
|
||||
* @param args - Variable arguments passed to the fetcher
|
||||
* @returns Data or a Promise that resolves to data
|
||||
* @public
|
||||
*/
|
||||
type BareFetcher<Data = unknown> = (...args: any[]) => FetcherResponse<Data>;
|
||||
/**
|
||||
* Typed fetcher function that is constrained by the SWR key type.
|
||||
*
|
||||
* Provides type safety by ensuring the fetcher argument matches the key type.
|
||||
* The conditional type logic ensures that:
|
||||
* - If the key is a function returning a value, the fetcher receives that value
|
||||
* - If the key is falsy (null, undefined, false), the fetcher is never called
|
||||
* - Otherwise, the fetcher receives the key directly as its argument
|
||||
*
|
||||
* @template Data - The type of data returned by the fetcher
|
||||
* @template SWRKey - The type of the SWR key, used to infer fetcher arguments
|
||||
* @public
|
||||
*/
|
||||
type Fetcher<Data = unknown, SWRKey extends Key = Key> = SWRKey extends () => (infer Arg) | null | undefined | false ? (arg: Arg) => FetcherResponse<Data> : SWRKey extends null | undefined | false ? never : SWRKey extends infer Arg ? (arg: Arg) => FetcherResponse<Data> : never;
|
||||
/**
|
||||
* Determines if data should block rendering based on suspense configuration.
|
||||
*
|
||||
* This conditional type is used internally to determine the return type of `data`
|
||||
* in SWRResponse. When suspense is enabled or fallbackData is provided, data
|
||||
* will never be undefined, allowing for non-nullable return types.
|
||||
*
|
||||
* The type resolution follows this logic:
|
||||
* 1. If global suspense is enabled → `true` (data never undefined)
|
||||
* 2. If no options provided → `false` (data can be undefined)
|
||||
* 3. If suspense is enabled in options → `true` (data never undefined)
|
||||
* 4. If fallbackData is provided → `true` (data never undefined)
|
||||
* 5. Otherwise → `false` (data can be undefined)
|
||||
*
|
||||
* @template Data - The data type
|
||||
* @template Options - The SWR configuration options
|
||||
* @returns `true` if data is guaranteed to be defined, `false` if it can be undefined
|
||||
* @internal
|
||||
*/
|
||||
type BlockingData<Data = any, Options = SWRDefaultOptions<Data>> = SWRGlobalConfig extends {
|
||||
suspense: true;
|
||||
} ? true : Options extends undefined ? false : Options extends {
|
||||
suspense: true;
|
||||
} ? true : Options extends {
|
||||
fallbackData: Data | Promise<Data>;
|
||||
} ? true : false;
|
||||
/**
|
||||
* Configuration types that are only used internally, not exposed to the user.
|
||||
*
|
||||
* These options are managed internally by SWR and passed between internal
|
||||
* functions. They are not part of the public API and should not be used
|
||||
* directly by consumers.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
interface InternalConfiguration {
|
||||
/** The cache instance used to store SWR data and state */
|
||||
cache: Cache;
|
||||
/** Scoped mutator function for updating cache entries */
|
||||
mutate: ScopedMutator;
|
||||
/** Unloader function bound to the cache for clearing all entries */
|
||||
unload: Unloader;
|
||||
}
|
||||
/**
|
||||
* Options for the `unload` function.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
interface UnloadOptions {
|
||||
/**
|
||||
* Whether to revalidate the keys of all mounted hooks after clearing the
|
||||
* cache. When disabled, mounted hooks stay in the empty state until the
|
||||
* next revalidation event (focus, reconnect, remount).
|
||||
* @defaultValue true
|
||||
*/
|
||||
revalidate?: boolean;
|
||||
}
|
||||
/**
|
||||
* Clears every entry in the cache — including the special `useSWRInfinite`
|
||||
* and `useSWRSubscription` keys that key filters can't match — discards all
|
||||
* in-flight requests and mutations, and notifies subscribers so mounted
|
||||
* hooks re-render with the empty cache. Previous data kept on screen by
|
||||
* `keepPreviousData` is dropped as well.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
type Unloader = (options?: UnloadOptions) => void;
|
||||
/**
|
||||
* Public configuration options for SWR.
|
||||
*
|
||||
* This interface defines all the configuration options that users can pass
|
||||
* to customize SWR's behavior. These options can be provided globally via
|
||||
* SWRConfig or per-hook via the config parameter.
|
||||
*
|
||||
* @template Data - The type of data returned by the fetcher
|
||||
* @template Error - The type of error that can be thrown
|
||||
* @template Fn - The fetcher function type
|
||||
*
|
||||
* @public
|
||||
* @see {@link https://swr.vercel.app/docs/options | SWR Options Documentation}
|
||||
*/
|
||||
interface PublicConfiguration<Data = any, Error = any, Fn extends Fetcher = BareFetcher> {
|
||||
/**
|
||||
* error retry interval in milliseconds
|
||||
* @defaultValue 5000
|
||||
*/
|
||||
errorRetryInterval: number;
|
||||
/** max error retry count */
|
||||
errorRetryCount?: number;
|
||||
/**
|
||||
* timeout to trigger the onLoadingSlow event in milliseconds
|
||||
* @defaultValue 3000
|
||||
*/
|
||||
loadingTimeout: number;
|
||||
/**
|
||||
* only revalidate once during a time span in milliseconds
|
||||
* @defaultValue 5000
|
||||
*/
|
||||
focusThrottleInterval: number;
|
||||
/**
|
||||
* dedupe requests with the same key in this time span in milliseconds
|
||||
* @defaultValue 2000
|
||||
*/
|
||||
dedupingInterval: number;
|
||||
/**
|
||||
* * Disabled by default: `refreshInterval = 0`
|
||||
* * If set to a number, polling interval in milliseconds
|
||||
* * If set to a function, the function will receive the latest data and should return the interval in milliseconds
|
||||
* @see {@link https://swr.vercel.app/docs/revalidation}
|
||||
*/
|
||||
refreshInterval?: number | ((latestData: Data | undefined) => number);
|
||||
/**
|
||||
* polling when the window is invisible (if `refreshInterval` is enabled)
|
||||
* @defaultValue false
|
||||
*/
|
||||
refreshWhenHidden?: boolean;
|
||||
/**
|
||||
* polling when the browser is offline (determined by `navigator.onLine`)
|
||||
*
|
||||
* When enabled, SWR will continue polling even when the browser is offline.
|
||||
* This can be useful for applications that need to check for connectivity
|
||||
* or cache updates while offline.
|
||||
*
|
||||
* @defaultValue false
|
||||
*/
|
||||
refreshWhenOffline?: boolean;
|
||||
/**
|
||||
* automatically revalidate when window gets focused
|
||||
*
|
||||
* When enabled, SWR will automatically revalidate data when the user
|
||||
* returns focus to the window/tab. This ensures data freshness when
|
||||
* users switch between applications.
|
||||
*
|
||||
* @defaultValue true
|
||||
* @see {@link https://swr.vercel.app/docs/revalidation | Revalidation Documentation}
|
||||
*/
|
||||
revalidateOnFocus: boolean;
|
||||
/**
|
||||
* automatically revalidate when the browser regains a network connection (via `navigator.onLine`)
|
||||
*
|
||||
* When enabled, SWR will automatically revalidate data when the browser
|
||||
* goes from offline to online state, ensuring data is up-to-date when
|
||||
* connectivity is restored.
|
||||
*
|
||||
* @defaultValue true
|
||||
* @see {@link https://swr.vercel.app/docs/revalidation | Revalidation Documentation}
|
||||
*/
|
||||
revalidateOnReconnect: boolean;
|
||||
/**
|
||||
* enable or disable automatic revalidation when component is mounted
|
||||
*
|
||||
* Controls whether SWR should automatically fetch data when the component
|
||||
* mounts. When `undefined`, the behavior depends on `revalidateIfStale`.
|
||||
*
|
||||
* @defaultValue undefined (inherits from revalidateIfStale)
|
||||
*/
|
||||
revalidateOnMount?: boolean;
|
||||
/**
|
||||
* automatically revalidate even if there is stale data
|
||||
* @defaultValue true
|
||||
* @see {@link https://swr.vercel.app/docs/revalidation#disable-automatic-revalidations}
|
||||
*/
|
||||
revalidateIfStale: boolean;
|
||||
/**
|
||||
* retry when fetcher has an error
|
||||
* @defaultValue true
|
||||
*/
|
||||
shouldRetryOnError: boolean | ((err: Error) => boolean);
|
||||
/**
|
||||
* keep the previous result when key is changed but data is not ready
|
||||
* @defaultValue false
|
||||
*/
|
||||
keepPreviousData?: boolean;
|
||||
/**
|
||||
* @experimental enable React Suspense mode
|
||||
* @defaultValue false
|
||||
* @see {@link https://swr.vercel.app/docs/suspense}
|
||||
*/
|
||||
suspense?: boolean;
|
||||
/**
|
||||
* initial data to be returned (note: ***This is per-hook***)
|
||||
* @see {@link https://swr.vercel.app/docs/with-nextjs}
|
||||
*/
|
||||
fallbackData?: Data | Promise<Data>;
|
||||
/**
|
||||
* warns when preload data is missing for a given key, this includes fallback
|
||||
* data, preload calls, or initial data from the cache provider
|
||||
* @defaultValue false
|
||||
*/
|
||||
strictServerPrefetchWarning?: boolean;
|
||||
/**
|
||||
* the fetcher function
|
||||
*/
|
||||
fetcher?: Fn;
|
||||
/**
|
||||
* array of middleware functions
|
||||
* @see {@link https://swr.vercel.app/docs/middleware}
|
||||
*/
|
||||
use?: Middleware[];
|
||||
/**
|
||||
* a key-value object of multiple fallback data
|
||||
* @see {@link https://swr.vercel.app/docs/with-nextjs#pre-rendering-with-default-data}
|
||||
*/
|
||||
fallback: {
|
||||
[key: string]: any;
|
||||
};
|
||||
/**
|
||||
* Function to detect whether pause revalidations, will ignore fetched data and errors when it returns true. Returns false by default.
|
||||
*/
|
||||
isPaused: () => boolean;
|
||||
/**
|
||||
* callback function when a request takes too long to load (see `loadingTimeout`)
|
||||
*/
|
||||
onLoadingSlow: (key: string, config: Readonly<PublicConfiguration<Data, Error, Fn>>) => void;
|
||||
/**
|
||||
* callback function when a request finishes successfully
|
||||
*/
|
||||
onSuccess: (data: Data, key: string, config: Readonly<PublicConfiguration<Data, Error, Fn>>) => void;
|
||||
/**
|
||||
* callback function when a request returns an error
|
||||
*/
|
||||
onError: (err: Error, key: string, config: Readonly<PublicConfiguration<Data, Error, Fn>>) => void;
|
||||
/**
|
||||
* handler for error retry
|
||||
*/
|
||||
onErrorRetry: (err: Error, key: string, config: Readonly<PublicConfiguration<Data, Error, Fn>>, revalidate: Revalidator, revalidateOpts: Required<RevalidatorOptions>) => void;
|
||||
/**
|
||||
* callback function when a request is ignored due to race conditions
|
||||
*/
|
||||
onDiscarded: (key: string) => void;
|
||||
/**
|
||||
* Comparison function used to detect when returned data has changed, to avoid spurious rerenders. By default, [dequal](https://github.com/lukeed/dequal) is used.
|
||||
*/
|
||||
compare: (a: Data | undefined, b: Data | undefined) => boolean;
|
||||
/**
|
||||
* IsOnline and isVisible are functions that return a boolean, to determine if the application is "active". By default, SWR will bail out a revalidation if these conditions are not met.
|
||||
* @see {@link https://swr.vercel.app/docs/advanced/react-native#customize-focus-and-reconnect-events}
|
||||
*/
|
||||
isOnline: () => boolean;
|
||||
/**
|
||||
* IsOnline and isVisible are functions that return a boolean, to determine if the application is "active". By default, SWR will bail out a revalidation if these conditions are not met.
|
||||
* @see {@link https://swr.vercel.app/docs/advanced/react-native#customize-focus-and-reconnect-events}
|
||||
*/
|
||||
isVisible: () => boolean;
|
||||
}
|
||||
type FullConfiguration<Data = any, Error = any, Fn extends Fetcher = BareFetcher> = InternalConfiguration & PublicConfiguration<Data, Error, Fn> & {
|
||||
/**
|
||||
* server-loaded data to be consumed by client hooks and written into cache
|
||||
* @experimental
|
||||
*/
|
||||
cacheData?: CacheData<Data>;
|
||||
};
|
||||
/**
|
||||
* Provider configuration for custom focus and reconnect event handling.
|
||||
*
|
||||
* This configuration allows custom implementations for detecting window focus
|
||||
* and network reconnection events. Useful for React Native or other environments
|
||||
* where the default browser APIs are not available.
|
||||
*
|
||||
* @public
|
||||
* @see {@link https://swr.vercel.app/docs/advanced/react-native | React Native Documentation}
|
||||
*/
|
||||
type ProviderConfiguration = {
|
||||
/**
|
||||
* Initialize focus event listener.
|
||||
*
|
||||
* @param callback - Function to call when window gains focus
|
||||
* @returns Optional cleanup function to remove the listener
|
||||
*/
|
||||
initFocus: (callback: () => void) => (() => void) | void;
|
||||
/**
|
||||
* Initialize reconnect event listener.
|
||||
*
|
||||
* @param callback - Function to call when network reconnects
|
||||
* @returns Optional cleanup function to remove the listener
|
||||
*/
|
||||
initReconnect: (callback: () => void) => (() => void) | void;
|
||||
};
|
||||
/**
|
||||
* The main useSWR hook interface with multiple overloads for different usage patterns.
|
||||
*
|
||||
* This interface provides type-safe overloads for various ways to call useSWR,
|
||||
* from simple key-only calls to complex configurations with custom fetchers.
|
||||
* The overloads ensure proper type inference for data, error, and configuration.
|
||||
*
|
||||
* @public
|
||||
*
|
||||
* @example Basic usage
|
||||
* ```ts
|
||||
* const { data, error } = useSWR('/api/data', fetcher)
|
||||
* ```
|
||||
*
|
||||
* @example With configuration
|
||||
* ```ts
|
||||
* const { data, error } = useSWR('/api/data', fetcher, {
|
||||
* refreshInterval: 1000,
|
||||
* revalidateOnFocus: false
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* @example Conditional fetching
|
||||
* ```ts
|
||||
* const { data, error } = useSWR(
|
||||
* user.id ? `/api/user/${user.id}` : null,
|
||||
* fetcher
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* @example Dynamic key with function
|
||||
* ```ts
|
||||
* const { data, error } = useSWR(
|
||||
* () => user.id ? [`/api/user/${user.id}`, user.token] : null,
|
||||
* ([url, token]) => fetcher(url, { headers: { Authorization: token } })
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
interface SWRHook {
|
||||
/**
|
||||
* Basic usage with just a key. Requires a global fetcher to be configured,
|
||||
* or can be used for client-side state management without fetching.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // With global fetcher
|
||||
* const { data } = useSWR('/api/user')
|
||||
*
|
||||
* // Client state management
|
||||
* const { data, mutate } = useSWR('user-settings')
|
||||
* mutate({ theme: 'dark' })
|
||||
* ```
|
||||
*/
|
||||
<Data = any, Error = any, SWRKey extends Key = StrictKey>(key: SWRKey): SWRResponse<Data, Error>;
|
||||
/**
|
||||
* Most common usage pattern with key and explicit fetcher function.
|
||||
* The fetcher receives the key as its argument and returns the data.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const { data, error } = useSWR('/api/user/123',
|
||||
* (url) => fetch(url).then(res => res.json())
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
<Data = any, Error = any, SWRKey extends Key = StrictKey>(key: SWRKey, fetcher: Fetcher<Data, SWRKey> | null): SWRResponse<Data, Error>;
|
||||
/**
|
||||
* Key with fetcher using relaxed key constraints for dynamic or complex keys.
|
||||
* Allows more flexible key types including functions and objects.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const { data } = useSWR(
|
||||
* () => user ? ['/api/posts', user.id] : null,
|
||||
* ([url, userId]) => fetchUserPosts(url, userId)
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
<Data = any, Error = any, SWRKey extends Key = Key>(key: SWRKey, fetcher: Fetcher<Data, SWRKey> | null): SWRResponse<Data, Error>;
|
||||
/**
|
||||
* Key-only with advanced configuration options and strict typing.
|
||||
* Useful when you need specific SWR options but rely on a global fetcher.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const { data } = useSWR<User>('/api/user', {
|
||||
* refreshInterval: 5000,
|
||||
* revalidateOnFocus: false
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
<Data = any, Error = any, SWRKey extends Key = StrictKey, SWROptions extends SWRConfiguration<Data, Error, Fetcher<Data, SWRKey>> | undefined = SWRConfiguration<Data, Error, Fetcher<Data, SWRKey>> | undefined>(key: SWRKey): SWRResponse<Data, Error, SWROptions>;
|
||||
/**
|
||||
* Key with fetcher and advanced configuration options with strict typing.
|
||||
* Provides full control over fetching behavior and SWR options.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const { data } = useSWR('/api/data', fetcher, {
|
||||
* suspense: true,
|
||||
* fallbackData: initialData
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
<Data = any, Error = any, SWRKey extends Key = StrictKey, SWROptions extends SWRConfiguration<Data, Error, Fetcher<Data, SWRKey>> | undefined = SWRConfiguration<Data, Error, Fetcher<Data, SWRKey>> | undefined>(key: SWRKey, fetcher: Fetcher<Data, SWRKey> | null): SWRResponse<Data, Error, SWROptions>;
|
||||
/**
|
||||
* Key with configuration object but no explicit fetcher. Uses global fetcher
|
||||
* or can be used for pure client state management with configuration.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // With global fetcher and config
|
||||
* const { data } = useSWR('/api/user', {
|
||||
* refreshInterval: 1000
|
||||
* })
|
||||
*
|
||||
* // Client state with config
|
||||
* const { data } = useSWR('local-state', {
|
||||
* fallbackData: defaultValue
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
<Data = any, Error = any, SWRKey extends Key = StrictKey, SWROptions extends SWRConfiguration<Data, Error, Fetcher<Data, SWRKey>> | undefined = SWRConfiguration<Data, Error, Fetcher<Data, SWRKey>> | undefined>(key: SWRKey, config: SWRConfigurationWithOptionalFallback<SWROptions>): SWRResponse<Data, Error, SWROptions>;
|
||||
/**
|
||||
* Complete signature with key, fetcher, and configuration options.
|
||||
* Provides maximum flexibility and control over all SWR behavior.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const { data, error, isLoading } = useSWR(
|
||||
* '/api/user',
|
||||
* async (url) => {
|
||||
* const res = await fetch(url)
|
||||
* if (!res.ok) throw new Error('Failed to fetch')
|
||||
* return res.json()
|
||||
* },
|
||||
* {
|
||||
* refreshInterval: 5000,
|
||||
* onError: (error) => console.error('SWR Error:', error),
|
||||
* fallbackData: { name: 'Loading...' }
|
||||
* }
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
<Data = any, Error = any, SWRKey extends Key = StrictKey, SWROptions extends SWRConfiguration<Data, Error, Fetcher<Data, SWRKey>> | undefined = SWRConfiguration<Data, Error, Fetcher<Data, SWRKey>> | undefined>(key: SWRKey, fetcher: Fetcher<Data, SWRKey> | null, config: SWRConfigurationWithOptionalFallback<SWROptions>): SWRResponse<Data, Error, SWROptions>;
|
||||
/**
|
||||
* Simple key-only usage with flexible key types. Most permissive overload
|
||||
* that accepts any valid key format.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const { data } = useSWR('/api/data')
|
||||
* const { data: userData } = useSWR(['user', userId])
|
||||
* const { data: settings } = useSWR({ endpoint: '/settings', version: 'v1' })
|
||||
* ```
|
||||
*/
|
||||
<Data = any, Error = any>(key: Key): SWRResponse<Data, Error>;
|
||||
/**
|
||||
* Key-only with configuration options using bare fetcher constraints.
|
||||
* Suitable for cases where fetcher type safety is less important.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const { data } = useSWR('/api/data', {
|
||||
* dedupingInterval: 5000
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
<Data = any, Error = any, SWROptions extends SWRConfiguration<Data, Error, BareFetcher<Data>> | undefined = SWRConfiguration<Data, Error, BareFetcher<Data>> | undefined>(key: Key): SWRResponse<Data, Error, SWROptions>;
|
||||
/**
|
||||
* Key with bare fetcher function that accepts any arguments.
|
||||
* Provides less type safety but maximum flexibility for fetcher signatures.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const { data } = useSWR('/api/user',
|
||||
* (...args) => customFetcher(...args)
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
<Data = any, Error = any, SWROptions extends SWRConfiguration<Data, Error, BareFetcher<Data>> | undefined = SWRConfiguration<Data, Error, BareFetcher<Data>> | undefined>(key: Key, fetcher: BareFetcher<Data> | null): SWRResponse<Data, Error, SWROptions>;
|
||||
/**
|
||||
* Key with configuration using relaxed fetcher typing constraints.
|
||||
* Useful when working with dynamic or loosely-typed fetcher functions.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const { data } = useSWR(dynamicKey, {
|
||||
* fetcher: customFetcher,
|
||||
* refreshInterval: 2000
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
<Data = any, Error = any, SWROptions extends SWRConfiguration<Data, Error, BareFetcher<Data>> | undefined = SWRConfiguration<Data, Error, BareFetcher<Data>> | undefined>(key: Key, config: SWRConfigurationWithOptionalFallback<SWROptions>): SWRResponse<Data, Error, SWROptions>;
|
||||
/**
|
||||
* Complete signature with key, bare fetcher, and configuration.
|
||||
* Most flexible overload with minimal type constraints, suitable for
|
||||
* complex scenarios where strict typing isn't feasible.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const { data } = useSWR(
|
||||
* complexKey,
|
||||
* (...args) => legacyFetcher(...args),
|
||||
* {
|
||||
* refreshInterval: 10000,
|
||||
* errorRetryCount: 3
|
||||
* }
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
<Data = any, Error = any, SWROptions extends SWRConfiguration<Data, Error, BareFetcher<Data>> | undefined = SWRConfiguration<Data, Error, BareFetcher<Data>> | undefined>(key: Key, fetcher: BareFetcher<Data> | null, config: SWRConfigurationWithOptionalFallback<SWROptions>): SWRResponse<Data, Error, SWROptions>;
|
||||
}
|
||||
/**
|
||||
* Middleware function type for extending SWR functionality.
|
||||
*
|
||||
* Middleware functions receive the next SWR hook in the chain and return
|
||||
* a modified hook function. This allows for composition of multiple
|
||||
* middleware functions to add features like logging, caching, or
|
||||
* request/response transformation.
|
||||
*
|
||||
* The middleware guarantees that a SWRHook receives a key, fetcher,
|
||||
* and config as arguments, providing a consistent interface for
|
||||
* middleware authors.
|
||||
*
|
||||
* @param useSWRNext - The next SWR hook function in the middleware chain
|
||||
* @returns A new SWR hook function with middleware functionality applied
|
||||
*
|
||||
* @template Data - The type of data returned by the fetcher
|
||||
* @template Error - The type of error that can be thrown
|
||||
*
|
||||
* @public
|
||||
* @see {@link https://swr.vercel.app/docs/middleware | Middleware Documentation}
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const logger: Middleware = (useSWRNext) => (key, fetcher, config) => {
|
||||
* console.log('SWR Request:', key)
|
||||
* return useSWRNext(key, fetcher, config)
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
type Middleware = (useSWRNext: SWRHook) => <Data = any, Error = any>(key: Key, fetcher: BareFetcher<Data> | null, config: SWRConfiguration<Data, Error, BareFetcher<Data>>) => SWRResponse<Data, Error>;
|
||||
/**
|
||||
* Represents a tuple of arguments that can be passed to a fetcher.
|
||||
*
|
||||
* The first element is typically the primary key (like a URL), followed
|
||||
* by additional parameters that affect the request (like query parameters,
|
||||
* headers, or request options).
|
||||
*
|
||||
*/
|
||||
type ArgumentsTuple = readonly [any, ...unknown[]];
|
||||
/**
|
||||
* Valid types for SWR keys.
|
||||
*
|
||||
* SWR keys identify unique requests and can be:
|
||||
* - `string`: Simple URL or identifier
|
||||
* - `ArgumentsTuple`: Array with URL and additional parameters
|
||||
* - `Record<any, any>`: Object that will be serialized
|
||||
* - `null | undefined | false`: Falsy values disable the request
|
||||
*
|
||||
* When a key is falsy, SWR will not make the request, allowing for
|
||||
* conditional fetching based on application state.
|
||||
*
|
||||
* @public
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // String key
|
||||
* useSWR('/api/users', fetcher)
|
||||
*
|
||||
* // Array key with parameters
|
||||
* useSWR(['/api/user', userId], ([url, id]) => fetcher(`${url}/${id}`))
|
||||
*
|
||||
* // Object key
|
||||
* useSWR({ url: '/api/data', params: { page: 1 } }, fetcher)
|
||||
*
|
||||
* // Conditional key
|
||||
* useSWR(userId ? `/api/user/${userId}` : null, fetcher)
|
||||
* ```
|
||||
*/
|
||||
type Arguments = string | ArgumentsTuple | Record<any, any> | null | undefined | false;
|
||||
/**
|
||||
* SWR key that can be static or a function that returns arguments.
|
||||
*
|
||||
* When a function is provided, it's called on each render to determine
|
||||
* the current key. This allows for dynamic keys based on component state
|
||||
* or props.
|
||||
*
|
||||
* @public
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // Static key
|
||||
* useSWR('/api/data', fetcher)
|
||||
*
|
||||
* // Dynamic key function
|
||||
* useSWR(() => user ? `/api/user/${user.id}` : null, fetcher)
|
||||
* ```
|
||||
*/
|
||||
type Key = Arguments | (() => Arguments);
|
||||
/**
|
||||
* Strict tuple key type that only allows tuples or falsy values.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
type StrictTupleKey = ArgumentsTuple | null | undefined | false;
|
||||
/**
|
||||
* Strict key type for internal use.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
type StrictKey = StrictTupleKey | (() => StrictTupleKey);
|
||||
/**
|
||||
* Callback function type for mutator operations.
|
||||
*
|
||||
* This function receives the current cached data and can return new data
|
||||
* to update the cache. It can be synchronous or asynchronous, and can
|
||||
* return undefined to indicate no change should be made.
|
||||
*
|
||||
* @template Data - The type of the cached data
|
||||
* @param currentData - The current data in the cache (may be undefined)
|
||||
* @returns New data to set, undefined for no change, or a Promise resolving to either
|
||||
*
|
||||
* @public
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // Increment a counter
|
||||
* mutate(key, (current: number = 0) => current + 1)
|
||||
*
|
||||
* // Async update
|
||||
* mutate(key, async (current) => {
|
||||
* const updated = await updateData(current)
|
||||
* return updated
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
type MutatorCallback<Data = any> = (currentData?: Data) => Promise<undefined | Data> | undefined | Data;
|
||||
/**
|
||||
* Options for configuring mutator behavior.
|
||||
*
|
||||
* These options control how the mutation affects the cache, revalidation,
|
||||
* and error handling behavior.
|
||||
*
|
||||
* @template Data - The type of the data related to the key
|
||||
* @template MutationData - The type of the data returned by the mutator
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
type MutatorOptions<Data = any, MutationData = Data> = {
|
||||
/**
|
||||
* Whether to revalidate the cache after mutation.
|
||||
*
|
||||
* Can be a boolean or a function that receives the new data and key
|
||||
* to determine whether revalidation should occur.
|
||||
*
|
||||
* @defaultValue true
|
||||
*/
|
||||
revalidate?: boolean | ((data: Data, key: Arguments) => boolean);
|
||||
/**
|
||||
* Whether and how to populate the cache with mutation results.
|
||||
*
|
||||
* - `false`: Don't update the cache
|
||||
* - `true`: Update cache with mutation result directly
|
||||
* - Function: Transform mutation result before caching
|
||||
*
|
||||
* @defaultValue true
|
||||
*/
|
||||
populateCache?: boolean | ((result: MutationData, currentData: Data | undefined) => Data);
|
||||
/**
|
||||
* Optimistic data to show immediately while mutation is pending.
|
||||
*
|
||||
* Can be the data directly or a function that computes it based on
|
||||
* current and displayed data. Useful for immediate UI feedback.
|
||||
*
|
||||
* @defaultValue undefined
|
||||
*/
|
||||
optimisticData?: Data | ((currentData: Data | undefined, displayedData: Data | undefined) => Data);
|
||||
/**
|
||||
* Whether to rollback optimistic updates on error.
|
||||
*
|
||||
* Can be a boolean or a function that receives the error to determine
|
||||
* whether rollback should occur.
|
||||
*
|
||||
* @defaultValue true
|
||||
*/
|
||||
rollbackOnError?: boolean | ((error: unknown) => boolean);
|
||||
/**
|
||||
* Whether to throw errors instead of returning them in the error field.
|
||||
*
|
||||
* When true, errors will be thrown and can be caught with try/catch.
|
||||
* When false, errors are returned in the response object.
|
||||
*
|
||||
* @defaultValue false
|
||||
*/
|
||||
throwOnError?: boolean;
|
||||
};
|
||||
type MutatorConfig = {
|
||||
revalidate?: boolean;
|
||||
populateCache?: boolean;
|
||||
};
|
||||
type Broadcaster<Data = any, Error = any> = (cache: Cache<Data>, key: string, data: Data, error?: Error, isValidating?: boolean, revalidate?: boolean, populateCache?: boolean) => Promise<Data>;
|
||||
/**
|
||||
* Internal state structure stored in the cache.
|
||||
*
|
||||
* This represents the complete state for a cache entry, including
|
||||
* data, error, and loading states. All fields are optional as they
|
||||
* may not be present depending on the request lifecycle.
|
||||
*
|
||||
* @template Data - The type of data stored
|
||||
* @template Error - The type of error that can occur
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
type State<Data = any, Error = any> = {
|
||||
/** The cached data, if available */
|
||||
data?: Data;
|
||||
/** The error object, if an error occurred */
|
||||
error?: Error;
|
||||
/** Whether a revalidation is currently in progress */
|
||||
isValidating?: boolean;
|
||||
/** Whether this is the initial load with no cached data */
|
||||
isLoading?: boolean;
|
||||
};
|
||||
type MutatorFn<Data = any> = (cache: Cache, key: Key, data?: Data | Promise<Data> | MutatorCallback<Data>, opts?: boolean | MutatorOptions<Data>) => Promise<Data | undefined>;
|
||||
type MutatorWrapper<Fn> = Fn extends (...args: [...infer Parameters]) => infer Result ? Parameters[3] extends boolean ? Result : Parameters[3] extends Required<Pick<MutatorOptions, 'populateCache'>> ? Parameters[3]['populateCache'] extends false ? never : Result : Result : never;
|
||||
type Mutator<Data = any> = MutatorWrapper<MutatorFn<Data>>;
|
||||
interface ScopedMutator {
|
||||
/**
|
||||
* @typeParam Data - The type of the data related to the key
|
||||
* @typeParam MutationData - The type of the data returned by the mutator
|
||||
*/
|
||||
<Data = any, MutationData = Data>(matcher: (key?: Arguments) => boolean, data?: MutationData | Promise<MutationData> | MutatorCallback<MutationData>, opts?: boolean | MutatorOptions<Data, MutationData>): Promise<Array<MutationData | undefined>>;
|
||||
/**
|
||||
* @typeParam Data - The type of the data related to the key
|
||||
* @typeParam MutationData - The type of the data returned by the mutator
|
||||
*/
|
||||
<Data = any, T = Data>(key: Arguments, data?: T | Promise<T> | MutatorCallback<T>, opts?: boolean | MutatorOptions<Data, T>): Promise<T | undefined>;
|
||||
}
|
||||
/**
|
||||
* @typeParam Data - The type of the data related to the key
|
||||
* @typeParam MutationData - The type of the data returned by the mutator
|
||||
*/
|
||||
type KeyedMutator<Data> = <MutationData = Data>(data?: Data | Promise<Data | undefined> | MutatorCallback<Data>, opts?: boolean | MutatorOptions<Data, MutationData>) => Promise<Data | MutationData | undefined>;
|
||||
type SWRConfiguration<Data = any, Error = any, Fn extends BareFetcher<any> = BareFetcher<any>> = Partial<PublicConfiguration<Data, Error, Fn>> & Partial<ProviderConfiguration> & {
|
||||
provider?: (cache: Readonly<Cache>) => Cache;
|
||||
cacheData?: never;
|
||||
};
|
||||
type SWRConfigValue<Data = any, Error = any, Fn extends BareFetcher<any> = BareFetcher<any>> = Omit<SWRConfiguration<Data, Error, Fn>, 'cacheData'> & {
|
||||
/**
|
||||
* server-loaded data to be consumed by client hooks and written into cache
|
||||
* @experimental
|
||||
*/
|
||||
cacheData?: CacheData<Data>;
|
||||
};
|
||||
type IsLoadingResponse<Data = any, Options = SWRDefaultOptions<Data>> = SWRGlobalConfig extends {
|
||||
suspense: true;
|
||||
} ? Options extends {
|
||||
suspense: true;
|
||||
} ? false : false : boolean;
|
||||
type SWRDefaultOptions<Data> = SWRConfiguration<Data, Error, Fetcher<Data, Key>>;
|
||||
type SWRConfigurationWithOptionalFallback<Options> = Options extends SWRConfiguration & Required<Pick<SWRConfiguration, 'fallbackData'>> ? Omit<Options, 'fallbackData'> & Pick<Partial<Options>, 'fallbackData'> : Options;
|
||||
/**
|
||||
* The response object returned by SWR hooks.
|
||||
*
|
||||
* This interface represents the return value of useSWR and related hooks,
|
||||
* providing access to data, error state, and control functions.
|
||||
*
|
||||
* @template Data - The type of data returned by the fetcher
|
||||
* @template Error - The type of error that can be thrown
|
||||
* @template Config - The configuration type used to determine blocking behavior
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
interface SWRResponse<Data = any, Error = any, Config = any> {
|
||||
/**
|
||||
* The data returned by the fetcher function.
|
||||
*
|
||||
* - When suspense is enabled or fallbackData is provided: always defined
|
||||
* - Otherwise: `undefined` during initial load or when key is falsy
|
||||
*/
|
||||
data: BlockingData<Data, Config> extends true ? Data : Data | undefined;
|
||||
/**
|
||||
* The error object thrown by the fetcher function.
|
||||
*
|
||||
* `undefined` when there's no error or when a request is in progress.
|
||||
*/
|
||||
error: Error | undefined;
|
||||
/**
|
||||
* Function to mutate the cached data for this specific key.
|
||||
*
|
||||
* This is a bound version of the global mutate function that automatically
|
||||
* uses the current key, providing type safety and convenience.
|
||||
*/
|
||||
mutate: KeyedMutator<Data>;
|
||||
/**
|
||||
* Whether the request is currently being validated (loading fresh data).
|
||||
*
|
||||
* `true` during initial load, revalidation, or when mutate is called
|
||||
* with a promise or async function.
|
||||
*/
|
||||
isValidating: boolean;
|
||||
/**
|
||||
* Whether the request is in initial loading state.
|
||||
*
|
||||
* `true` only during the initial load when there's no cached data.
|
||||
* Unlike `isValidating`, this becomes `false` once data is available.
|
||||
*/
|
||||
isLoading: IsLoadingResponse<Data, Config>;
|
||||
}
|
||||
type KeyLoader<Args extends Arguments = Arguments> = ((index: number, previousPageData: any | null) => Args) | null;
|
||||
interface RevalidatorOptions {
|
||||
retryCount?: number;
|
||||
dedupe?: boolean;
|
||||
}
|
||||
type Revalidator = (revalidateOpts?: RevalidatorOptions) => Promise<boolean> | void;
|
||||
type RevalidateEvent = typeof FOCUS_EVENT | typeof RECONNECT_EVENT | typeof MUTATE_EVENT | typeof ERROR_REVALIDATE_EVENT | typeof UNLOAD_EVENT;
|
||||
type RevalidateCallbackReturnType = {
|
||||
[FOCUS_EVENT]: void;
|
||||
[RECONNECT_EVENT]: void;
|
||||
[MUTATE_EVENT]: Promise<boolean>;
|
||||
[ERROR_REVALIDATE_EVENT]: void;
|
||||
[UNLOAD_EVENT]: void;
|
||||
};
|
||||
type RevalidateCallback = <K extends RevalidateEvent>(type: K, opts?: any) => RevalidateCallbackReturnType[K];
|
||||
/**
|
||||
* Cache interface for storing SWR state.
|
||||
*
|
||||
* This interface defines the contract for cache providers used by SWR.
|
||||
* The default implementation uses a Map, but custom cache providers
|
||||
* can implement this interface to provide different storage mechanisms.
|
||||
*
|
||||
* @template Data - The type of data stored in the cache
|
||||
*
|
||||
* @public
|
||||
* @see {@link https://swr.vercel.app/docs/advanced/cache | Cache Documentation}
|
||||
*/
|
||||
interface Cache<Data = any> {
|
||||
/**
|
||||
* Get an iterator of all cache keys.
|
||||
*
|
||||
* @returns Iterator that yields all cache keys as strings
|
||||
*/
|
||||
keys(): IterableIterator<string>;
|
||||
/**
|
||||
* Get the cached state for a key.
|
||||
*
|
||||
* @param key - The cache key to look up
|
||||
* @returns The cached state or undefined if not found
|
||||
*/
|
||||
get(key: string): State<Data> | undefined;
|
||||
/**
|
||||
* Set the cached state for a key.
|
||||
*
|
||||
* @param key - The cache key to set
|
||||
* @param value - The state to cache
|
||||
*/
|
||||
set(key: string, value: State<Data>): void;
|
||||
/**
|
||||
* Delete a cached entry.
|
||||
*
|
||||
* @param key - The cache key to delete
|
||||
*/
|
||||
delete(key: string): void;
|
||||
}
|
||||
interface StateDependencies {
|
||||
data?: boolean;
|
||||
error?: boolean;
|
||||
isValidating?: boolean;
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
export { cache as H, compare as J, defaultConfig as L, mutate as N, preload as O, unload as Q, SWRConfig$1 as S, useSWRConfig as T, SWRConfig as V, useSWR as X, unstable_serialize as u };
|
||||
export type { Arguments as A, BareFetcher as B, CacheData as C, StateDependencies as D, UnloadOptions as E, FetcherResponse as F, GlobalState as G, InternalConfiguration as I, Key as K, Middleware as M, ProviderConfiguration as P, RevalidateCallback as R, Unloader as U, SWRGlobalConfig as W, SWRHook as a, SWRConfigValue as b, SWRResponse as c, MutatorCallback as d, SWRConfiguration as e, StrictTupleKey as f, MutatorOptions as g, Cache as h, ScopedMutator as i, State as j, FullConfiguration as k, Fetcher as l, BlockingData as m, Broadcaster as n, IsLoadingResponse as o, KeyLoader as p, KeyedMutator as q, Mutator as r, MutatorConfig as s, MutatorFn as t, MutatorWrapper as v, PublicConfiguration as w, RevalidateEvent as x, Revalidator as y, RevalidatorOptions as z };
|
||||
+971
@@ -0,0 +1,971 @@
|
||||
import { FC, PropsWithChildren } from 'react';
|
||||
import { F as FOCUS_EVENT, R as RECONNECT_EVENT, M as MUTATE_EVENT, E as ERROR_REVALIDATE_EVENT, U as UNLOAD_EVENT } from './events-ny18dyc2.js';
|
||||
import { dequal } from 'dequal/lite';
|
||||
|
||||
declare const SWRConfig$1: FC<PropsWithChildren<{
|
||||
value?: SWRConfigValue | ((parentConfig?: SWRConfigValue) => SWRConfigValue);
|
||||
}>>;
|
||||
|
||||
declare const compare: typeof dequal;
|
||||
declare const cache: Cache<any>;
|
||||
declare const mutate: ScopedMutator;
|
||||
declare const unload: Unloader;
|
||||
|
||||
declare const defaultConfig: FullConfiguration;
|
||||
|
||||
declare const useSWRConfig: () => FullConfiguration;
|
||||
|
||||
type PreloadFetcher<Data = unknown, SWRKey extends Key = Key> = SWRKey extends () => infer Arg ? (arg: Arg) => FetcherResponse<Data> : SWRKey extends infer Arg ? (arg: Arg) => FetcherResponse<Data> : never;
|
||||
declare const preload: <Data = any, SWRKey extends Key = Key, Fetcher extends BareFetcher = PreloadFetcher<Data, SWRKey>>(key_: SWRKey, fetcher: Fetcher) => ReturnType<Fetcher>;
|
||||
|
||||
declare const unstable_serialize: (key: Key) => string;
|
||||
|
||||
declare const SWRConfig: typeof SWRConfig$1 & {
|
||||
defaultValue: FullConfiguration;
|
||||
};
|
||||
|
||||
/**
|
||||
* A hook to fetch data.
|
||||
*
|
||||
* @see {@link https://swr.vercel.app}
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
* import useSWR from 'swr'
|
||||
* function Profile() {
|
||||
* const { data, error, isLoading } = useSWR('/api/user', fetcher)
|
||||
* if (error) return <div>failed to load</div>
|
||||
* if (isLoading) return <div>loading...</div>
|
||||
* return <div>hello {data.name}!</div>
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
declare const useSWR: SWRHook;
|
||||
|
||||
interface SWRGlobalConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Global state tuple containing SWR's internal state management structures.
|
||||
*
|
||||
* This is the core state structure that manages all SWR operations internally.
|
||||
* Each element serves a specific purpose in the SWR ecosystem.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
type GlobalState = [
|
||||
/** Event revalidators: Maps cache keys to arrays of revalidation callbacks */
|
||||
Record<string, RevalidateCallback[]>,
|
||||
/** Mutation timestamps: Maps cache keys to [start_timestamp, end_timestamp] tuples */
|
||||
Record<string, [number, number]>,
|
||||
/** Fetch cache: Maps cache keys to [data, timestamp] tuples */
|
||||
Record<string, [any, number]>,
|
||||
/** Preload cache: Maps cache keys to fetcher responses */
|
||||
Record<string, FetcherResponse<any>>,
|
||||
/** Scoped mutator function for cache updates */
|
||||
ScopedMutator,
|
||||
/** Cache setter function with prev/current value comparison */
|
||||
(key: string, value: any, prev: any) => void,
|
||||
/** Cache subscriber function that returns an unsubscribe function */
|
||||
(key: string, callback: (current: any, prev: any) => void) => () => void,
|
||||
/** Unloader function that clears the cache and notifies subscribers */
|
||||
Unloader,
|
||||
/** Unload generation, bumped on every unload to discard in-flight writes */
|
||||
number
|
||||
];
|
||||
/**
|
||||
* Response type that can be returned by fetcher functions.
|
||||
*
|
||||
* @template Data - The type of data returned by the fetcher
|
||||
* @public
|
||||
*/
|
||||
type FetcherResponse<Data = unknown> = Data | Promise<Data>;
|
||||
/**
|
||||
* Cache data produced on the server and consumed by SWRConfig on the client.
|
||||
*
|
||||
* @experimental
|
||||
* @public
|
||||
*/
|
||||
type CacheData<Data = any> = {
|
||||
[key: string]: FetcherResponse<Data>;
|
||||
};
|
||||
/**
|
||||
* Basic fetcher function that accepts any arguments and returns data or a promise.
|
||||
*
|
||||
* This is the most permissive fetcher type, allowing any number of arguments
|
||||
* of any type. Used when type safety is not required or when dealing with
|
||||
* dynamic fetcher signatures.
|
||||
*
|
||||
* @template Data - The type of data returned by the fetcher
|
||||
* @param args - Variable arguments passed to the fetcher
|
||||
* @returns Data or a Promise that resolves to data
|
||||
* @public
|
||||
*/
|
||||
type BareFetcher<Data = unknown> = (...args: any[]) => FetcherResponse<Data>;
|
||||
/**
|
||||
* Typed fetcher function that is constrained by the SWR key type.
|
||||
*
|
||||
* Provides type safety by ensuring the fetcher argument matches the key type.
|
||||
* The conditional type logic ensures that:
|
||||
* - If the key is a function returning a value, the fetcher receives that value
|
||||
* - If the key is falsy (null, undefined, false), the fetcher is never called
|
||||
* - Otherwise, the fetcher receives the key directly as its argument
|
||||
*
|
||||
* @template Data - The type of data returned by the fetcher
|
||||
* @template SWRKey - The type of the SWR key, used to infer fetcher arguments
|
||||
* @public
|
||||
*/
|
||||
type Fetcher<Data = unknown, SWRKey extends Key = Key> = SWRKey extends () => (infer Arg) | null | undefined | false ? (arg: Arg) => FetcherResponse<Data> : SWRKey extends null | undefined | false ? never : SWRKey extends infer Arg ? (arg: Arg) => FetcherResponse<Data> : never;
|
||||
/**
|
||||
* Determines if data should block rendering based on suspense configuration.
|
||||
*
|
||||
* This conditional type is used internally to determine the return type of `data`
|
||||
* in SWRResponse. When suspense is enabled or fallbackData is provided, data
|
||||
* will never be undefined, allowing for non-nullable return types.
|
||||
*
|
||||
* The type resolution follows this logic:
|
||||
* 1. If global suspense is enabled → `true` (data never undefined)
|
||||
* 2. If no options provided → `false` (data can be undefined)
|
||||
* 3. If suspense is enabled in options → `true` (data never undefined)
|
||||
* 4. If fallbackData is provided → `true` (data never undefined)
|
||||
* 5. Otherwise → `false` (data can be undefined)
|
||||
*
|
||||
* @template Data - The data type
|
||||
* @template Options - The SWR configuration options
|
||||
* @returns `true` if data is guaranteed to be defined, `false` if it can be undefined
|
||||
* @internal
|
||||
*/
|
||||
type BlockingData<Data = any, Options = SWRDefaultOptions<Data>> = SWRGlobalConfig extends {
|
||||
suspense: true;
|
||||
} ? true : Options extends undefined ? false : Options extends {
|
||||
suspense: true;
|
||||
} ? true : Options extends {
|
||||
fallbackData: Data | Promise<Data>;
|
||||
} ? true : false;
|
||||
/**
|
||||
* Configuration types that are only used internally, not exposed to the user.
|
||||
*
|
||||
* These options are managed internally by SWR and passed between internal
|
||||
* functions. They are not part of the public API and should not be used
|
||||
* directly by consumers.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
interface InternalConfiguration {
|
||||
/** The cache instance used to store SWR data and state */
|
||||
cache: Cache;
|
||||
/** Scoped mutator function for updating cache entries */
|
||||
mutate: ScopedMutator;
|
||||
/** Unloader function bound to the cache for clearing all entries */
|
||||
unload: Unloader;
|
||||
}
|
||||
/**
|
||||
* Options for the `unload` function.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
interface UnloadOptions {
|
||||
/**
|
||||
* Whether to revalidate the keys of all mounted hooks after clearing the
|
||||
* cache. When disabled, mounted hooks stay in the empty state until the
|
||||
* next revalidation event (focus, reconnect, remount).
|
||||
* @defaultValue true
|
||||
*/
|
||||
revalidate?: boolean;
|
||||
}
|
||||
/**
|
||||
* Clears every entry in the cache — including the special `useSWRInfinite`
|
||||
* and `useSWRSubscription` keys that key filters can't match — discards all
|
||||
* in-flight requests and mutations, and notifies subscribers so mounted
|
||||
* hooks re-render with the empty cache. Previous data kept on screen by
|
||||
* `keepPreviousData` is dropped as well.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
type Unloader = (options?: UnloadOptions) => void;
|
||||
/**
|
||||
* Public configuration options for SWR.
|
||||
*
|
||||
* This interface defines all the configuration options that users can pass
|
||||
* to customize SWR's behavior. These options can be provided globally via
|
||||
* SWRConfig or per-hook via the config parameter.
|
||||
*
|
||||
* @template Data - The type of data returned by the fetcher
|
||||
* @template Error - The type of error that can be thrown
|
||||
* @template Fn - The fetcher function type
|
||||
*
|
||||
* @public
|
||||
* @see {@link https://swr.vercel.app/docs/options | SWR Options Documentation}
|
||||
*/
|
||||
interface PublicConfiguration<Data = any, Error = any, Fn extends Fetcher = BareFetcher> {
|
||||
/**
|
||||
* error retry interval in milliseconds
|
||||
* @defaultValue 5000
|
||||
*/
|
||||
errorRetryInterval: number;
|
||||
/** max error retry count */
|
||||
errorRetryCount?: number;
|
||||
/**
|
||||
* timeout to trigger the onLoadingSlow event in milliseconds
|
||||
* @defaultValue 3000
|
||||
*/
|
||||
loadingTimeout: number;
|
||||
/**
|
||||
* only revalidate once during a time span in milliseconds
|
||||
* @defaultValue 5000
|
||||
*/
|
||||
focusThrottleInterval: number;
|
||||
/**
|
||||
* dedupe requests with the same key in this time span in milliseconds
|
||||
* @defaultValue 2000
|
||||
*/
|
||||
dedupingInterval: number;
|
||||
/**
|
||||
* * Disabled by default: `refreshInterval = 0`
|
||||
* * If set to a number, polling interval in milliseconds
|
||||
* * If set to a function, the function will receive the latest data and should return the interval in milliseconds
|
||||
* @see {@link https://swr.vercel.app/docs/revalidation}
|
||||
*/
|
||||
refreshInterval?: number | ((latestData: Data | undefined) => number);
|
||||
/**
|
||||
* polling when the window is invisible (if `refreshInterval` is enabled)
|
||||
* @defaultValue false
|
||||
*/
|
||||
refreshWhenHidden?: boolean;
|
||||
/**
|
||||
* polling when the browser is offline (determined by `navigator.onLine`)
|
||||
*
|
||||
* When enabled, SWR will continue polling even when the browser is offline.
|
||||
* This can be useful for applications that need to check for connectivity
|
||||
* or cache updates while offline.
|
||||
*
|
||||
* @defaultValue false
|
||||
*/
|
||||
refreshWhenOffline?: boolean;
|
||||
/**
|
||||
* automatically revalidate when window gets focused
|
||||
*
|
||||
* When enabled, SWR will automatically revalidate data when the user
|
||||
* returns focus to the window/tab. This ensures data freshness when
|
||||
* users switch between applications.
|
||||
*
|
||||
* @defaultValue true
|
||||
* @see {@link https://swr.vercel.app/docs/revalidation | Revalidation Documentation}
|
||||
*/
|
||||
revalidateOnFocus: boolean;
|
||||
/**
|
||||
* automatically revalidate when the browser regains a network connection (via `navigator.onLine`)
|
||||
*
|
||||
* When enabled, SWR will automatically revalidate data when the browser
|
||||
* goes from offline to online state, ensuring data is up-to-date when
|
||||
* connectivity is restored.
|
||||
*
|
||||
* @defaultValue true
|
||||
* @see {@link https://swr.vercel.app/docs/revalidation | Revalidation Documentation}
|
||||
*/
|
||||
revalidateOnReconnect: boolean;
|
||||
/**
|
||||
* enable or disable automatic revalidation when component is mounted
|
||||
*
|
||||
* Controls whether SWR should automatically fetch data when the component
|
||||
* mounts. When `undefined`, the behavior depends on `revalidateIfStale`.
|
||||
*
|
||||
* @defaultValue undefined (inherits from revalidateIfStale)
|
||||
*/
|
||||
revalidateOnMount?: boolean;
|
||||
/**
|
||||
* automatically revalidate even if there is stale data
|
||||
* @defaultValue true
|
||||
* @see {@link https://swr.vercel.app/docs/revalidation#disable-automatic-revalidations}
|
||||
*/
|
||||
revalidateIfStale: boolean;
|
||||
/**
|
||||
* retry when fetcher has an error
|
||||
* @defaultValue true
|
||||
*/
|
||||
shouldRetryOnError: boolean | ((err: Error) => boolean);
|
||||
/**
|
||||
* keep the previous result when key is changed but data is not ready
|
||||
* @defaultValue false
|
||||
*/
|
||||
keepPreviousData?: boolean;
|
||||
/**
|
||||
* @experimental enable React Suspense mode
|
||||
* @defaultValue false
|
||||
* @see {@link https://swr.vercel.app/docs/suspense}
|
||||
*/
|
||||
suspense?: boolean;
|
||||
/**
|
||||
* initial data to be returned (note: ***This is per-hook***)
|
||||
* @see {@link https://swr.vercel.app/docs/with-nextjs}
|
||||
*/
|
||||
fallbackData?: Data | Promise<Data>;
|
||||
/**
|
||||
* warns when preload data is missing for a given key, this includes fallback
|
||||
* data, preload calls, or initial data from the cache provider
|
||||
* @defaultValue false
|
||||
*/
|
||||
strictServerPrefetchWarning?: boolean;
|
||||
/**
|
||||
* the fetcher function
|
||||
*/
|
||||
fetcher?: Fn;
|
||||
/**
|
||||
* array of middleware functions
|
||||
* @see {@link https://swr.vercel.app/docs/middleware}
|
||||
*/
|
||||
use?: Middleware[];
|
||||
/**
|
||||
* a key-value object of multiple fallback data
|
||||
* @see {@link https://swr.vercel.app/docs/with-nextjs#pre-rendering-with-default-data}
|
||||
*/
|
||||
fallback: {
|
||||
[key: string]: any;
|
||||
};
|
||||
/**
|
||||
* Function to detect whether pause revalidations, will ignore fetched data and errors when it returns true. Returns false by default.
|
||||
*/
|
||||
isPaused: () => boolean;
|
||||
/**
|
||||
* callback function when a request takes too long to load (see `loadingTimeout`)
|
||||
*/
|
||||
onLoadingSlow: (key: string, config: Readonly<PublicConfiguration<Data, Error, Fn>>) => void;
|
||||
/**
|
||||
* callback function when a request finishes successfully
|
||||
*/
|
||||
onSuccess: (data: Data, key: string, config: Readonly<PublicConfiguration<Data, Error, Fn>>) => void;
|
||||
/**
|
||||
* callback function when a request returns an error
|
||||
*/
|
||||
onError: (err: Error, key: string, config: Readonly<PublicConfiguration<Data, Error, Fn>>) => void;
|
||||
/**
|
||||
* handler for error retry
|
||||
*/
|
||||
onErrorRetry: (err: Error, key: string, config: Readonly<PublicConfiguration<Data, Error, Fn>>, revalidate: Revalidator, revalidateOpts: Required<RevalidatorOptions>) => void;
|
||||
/**
|
||||
* callback function when a request is ignored due to race conditions
|
||||
*/
|
||||
onDiscarded: (key: string) => void;
|
||||
/**
|
||||
* Comparison function used to detect when returned data has changed, to avoid spurious rerenders. By default, [dequal](https://github.com/lukeed/dequal) is used.
|
||||
*/
|
||||
compare: (a: Data | undefined, b: Data | undefined) => boolean;
|
||||
/**
|
||||
* IsOnline and isVisible are functions that return a boolean, to determine if the application is "active". By default, SWR will bail out a revalidation if these conditions are not met.
|
||||
* @see {@link https://swr.vercel.app/docs/advanced/react-native#customize-focus-and-reconnect-events}
|
||||
*/
|
||||
isOnline: () => boolean;
|
||||
/**
|
||||
* IsOnline and isVisible are functions that return a boolean, to determine if the application is "active". By default, SWR will bail out a revalidation if these conditions are not met.
|
||||
* @see {@link https://swr.vercel.app/docs/advanced/react-native#customize-focus-and-reconnect-events}
|
||||
*/
|
||||
isVisible: () => boolean;
|
||||
}
|
||||
type FullConfiguration<Data = any, Error = any, Fn extends Fetcher = BareFetcher> = InternalConfiguration & PublicConfiguration<Data, Error, Fn> & {
|
||||
/**
|
||||
* server-loaded data to be consumed by client hooks and written into cache
|
||||
* @experimental
|
||||
*/
|
||||
cacheData?: CacheData<Data>;
|
||||
};
|
||||
/**
|
||||
* Provider configuration for custom focus and reconnect event handling.
|
||||
*
|
||||
* This configuration allows custom implementations for detecting window focus
|
||||
* and network reconnection events. Useful for React Native or other environments
|
||||
* where the default browser APIs are not available.
|
||||
*
|
||||
* @public
|
||||
* @see {@link https://swr.vercel.app/docs/advanced/react-native | React Native Documentation}
|
||||
*/
|
||||
type ProviderConfiguration = {
|
||||
/**
|
||||
* Initialize focus event listener.
|
||||
*
|
||||
* @param callback - Function to call when window gains focus
|
||||
* @returns Optional cleanup function to remove the listener
|
||||
*/
|
||||
initFocus: (callback: () => void) => (() => void) | void;
|
||||
/**
|
||||
* Initialize reconnect event listener.
|
||||
*
|
||||
* @param callback - Function to call when network reconnects
|
||||
* @returns Optional cleanup function to remove the listener
|
||||
*/
|
||||
initReconnect: (callback: () => void) => (() => void) | void;
|
||||
};
|
||||
/**
|
||||
* The main useSWR hook interface with multiple overloads for different usage patterns.
|
||||
*
|
||||
* This interface provides type-safe overloads for various ways to call useSWR,
|
||||
* from simple key-only calls to complex configurations with custom fetchers.
|
||||
* The overloads ensure proper type inference for data, error, and configuration.
|
||||
*
|
||||
* @public
|
||||
*
|
||||
* @example Basic usage
|
||||
* ```ts
|
||||
* const { data, error } = useSWR('/api/data', fetcher)
|
||||
* ```
|
||||
*
|
||||
* @example With configuration
|
||||
* ```ts
|
||||
* const { data, error } = useSWR('/api/data', fetcher, {
|
||||
* refreshInterval: 1000,
|
||||
* revalidateOnFocus: false
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* @example Conditional fetching
|
||||
* ```ts
|
||||
* const { data, error } = useSWR(
|
||||
* user.id ? `/api/user/${user.id}` : null,
|
||||
* fetcher
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* @example Dynamic key with function
|
||||
* ```ts
|
||||
* const { data, error } = useSWR(
|
||||
* () => user.id ? [`/api/user/${user.id}`, user.token] : null,
|
||||
* ([url, token]) => fetcher(url, { headers: { Authorization: token } })
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
interface SWRHook {
|
||||
/**
|
||||
* Basic usage with just a key. Requires a global fetcher to be configured,
|
||||
* or can be used for client-side state management without fetching.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // With global fetcher
|
||||
* const { data } = useSWR('/api/user')
|
||||
*
|
||||
* // Client state management
|
||||
* const { data, mutate } = useSWR('user-settings')
|
||||
* mutate({ theme: 'dark' })
|
||||
* ```
|
||||
*/
|
||||
<Data = any, Error = any, SWRKey extends Key = StrictKey>(key: SWRKey): SWRResponse<Data, Error>;
|
||||
/**
|
||||
* Most common usage pattern with key and explicit fetcher function.
|
||||
* The fetcher receives the key as its argument and returns the data.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const { data, error } = useSWR('/api/user/123',
|
||||
* (url) => fetch(url).then(res => res.json())
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
<Data = any, Error = any, SWRKey extends Key = StrictKey>(key: SWRKey, fetcher: Fetcher<Data, SWRKey> | null): SWRResponse<Data, Error>;
|
||||
/**
|
||||
* Key with fetcher using relaxed key constraints for dynamic or complex keys.
|
||||
* Allows more flexible key types including functions and objects.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const { data } = useSWR(
|
||||
* () => user ? ['/api/posts', user.id] : null,
|
||||
* ([url, userId]) => fetchUserPosts(url, userId)
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
<Data = any, Error = any, SWRKey extends Key = Key>(key: SWRKey, fetcher: Fetcher<Data, SWRKey> | null): SWRResponse<Data, Error>;
|
||||
/**
|
||||
* Key-only with advanced configuration options and strict typing.
|
||||
* Useful when you need specific SWR options but rely on a global fetcher.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const { data } = useSWR<User>('/api/user', {
|
||||
* refreshInterval: 5000,
|
||||
* revalidateOnFocus: false
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
<Data = any, Error = any, SWRKey extends Key = StrictKey, SWROptions extends SWRConfiguration<Data, Error, Fetcher<Data, SWRKey>> | undefined = SWRConfiguration<Data, Error, Fetcher<Data, SWRKey>> | undefined>(key: SWRKey): SWRResponse<Data, Error, SWROptions>;
|
||||
/**
|
||||
* Key with fetcher and advanced configuration options with strict typing.
|
||||
* Provides full control over fetching behavior and SWR options.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const { data } = useSWR('/api/data', fetcher, {
|
||||
* suspense: true,
|
||||
* fallbackData: initialData
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
<Data = any, Error = any, SWRKey extends Key = StrictKey, SWROptions extends SWRConfiguration<Data, Error, Fetcher<Data, SWRKey>> | undefined = SWRConfiguration<Data, Error, Fetcher<Data, SWRKey>> | undefined>(key: SWRKey, fetcher: Fetcher<Data, SWRKey> | null): SWRResponse<Data, Error, SWROptions>;
|
||||
/**
|
||||
* Key with configuration object but no explicit fetcher. Uses global fetcher
|
||||
* or can be used for pure client state management with configuration.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // With global fetcher and config
|
||||
* const { data } = useSWR('/api/user', {
|
||||
* refreshInterval: 1000
|
||||
* })
|
||||
*
|
||||
* // Client state with config
|
||||
* const { data } = useSWR('local-state', {
|
||||
* fallbackData: defaultValue
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
<Data = any, Error = any, SWRKey extends Key = StrictKey, SWROptions extends SWRConfiguration<Data, Error, Fetcher<Data, SWRKey>> | undefined = SWRConfiguration<Data, Error, Fetcher<Data, SWRKey>> | undefined>(key: SWRKey, config: SWRConfigurationWithOptionalFallback<SWROptions>): SWRResponse<Data, Error, SWROptions>;
|
||||
/**
|
||||
* Complete signature with key, fetcher, and configuration options.
|
||||
* Provides maximum flexibility and control over all SWR behavior.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const { data, error, isLoading } = useSWR(
|
||||
* '/api/user',
|
||||
* async (url) => {
|
||||
* const res = await fetch(url)
|
||||
* if (!res.ok) throw new Error('Failed to fetch')
|
||||
* return res.json()
|
||||
* },
|
||||
* {
|
||||
* refreshInterval: 5000,
|
||||
* onError: (error) => console.error('SWR Error:', error),
|
||||
* fallbackData: { name: 'Loading...' }
|
||||
* }
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
<Data = any, Error = any, SWRKey extends Key = StrictKey, SWROptions extends SWRConfiguration<Data, Error, Fetcher<Data, SWRKey>> | undefined = SWRConfiguration<Data, Error, Fetcher<Data, SWRKey>> | undefined>(key: SWRKey, fetcher: Fetcher<Data, SWRKey> | null, config: SWRConfigurationWithOptionalFallback<SWROptions>): SWRResponse<Data, Error, SWROptions>;
|
||||
/**
|
||||
* Simple key-only usage with flexible key types. Most permissive overload
|
||||
* that accepts any valid key format.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const { data } = useSWR('/api/data')
|
||||
* const { data: userData } = useSWR(['user', userId])
|
||||
* const { data: settings } = useSWR({ endpoint: '/settings', version: 'v1' })
|
||||
* ```
|
||||
*/
|
||||
<Data = any, Error = any>(key: Key): SWRResponse<Data, Error>;
|
||||
/**
|
||||
* Key-only with configuration options using bare fetcher constraints.
|
||||
* Suitable for cases where fetcher type safety is less important.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const { data } = useSWR('/api/data', {
|
||||
* dedupingInterval: 5000
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
<Data = any, Error = any, SWROptions extends SWRConfiguration<Data, Error, BareFetcher<Data>> | undefined = SWRConfiguration<Data, Error, BareFetcher<Data>> | undefined>(key: Key): SWRResponse<Data, Error, SWROptions>;
|
||||
/**
|
||||
* Key with bare fetcher function that accepts any arguments.
|
||||
* Provides less type safety but maximum flexibility for fetcher signatures.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const { data } = useSWR('/api/user',
|
||||
* (...args) => customFetcher(...args)
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
<Data = any, Error = any, SWROptions extends SWRConfiguration<Data, Error, BareFetcher<Data>> | undefined = SWRConfiguration<Data, Error, BareFetcher<Data>> | undefined>(key: Key, fetcher: BareFetcher<Data> | null): SWRResponse<Data, Error, SWROptions>;
|
||||
/**
|
||||
* Key with configuration using relaxed fetcher typing constraints.
|
||||
* Useful when working with dynamic or loosely-typed fetcher functions.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const { data } = useSWR(dynamicKey, {
|
||||
* fetcher: customFetcher,
|
||||
* refreshInterval: 2000
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
<Data = any, Error = any, SWROptions extends SWRConfiguration<Data, Error, BareFetcher<Data>> | undefined = SWRConfiguration<Data, Error, BareFetcher<Data>> | undefined>(key: Key, config: SWRConfigurationWithOptionalFallback<SWROptions>): SWRResponse<Data, Error, SWROptions>;
|
||||
/**
|
||||
* Complete signature with key, bare fetcher, and configuration.
|
||||
* Most flexible overload with minimal type constraints, suitable for
|
||||
* complex scenarios where strict typing isn't feasible.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const { data } = useSWR(
|
||||
* complexKey,
|
||||
* (...args) => legacyFetcher(...args),
|
||||
* {
|
||||
* refreshInterval: 10000,
|
||||
* errorRetryCount: 3
|
||||
* }
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
<Data = any, Error = any, SWROptions extends SWRConfiguration<Data, Error, BareFetcher<Data>> | undefined = SWRConfiguration<Data, Error, BareFetcher<Data>> | undefined>(key: Key, fetcher: BareFetcher<Data> | null, config: SWRConfigurationWithOptionalFallback<SWROptions>): SWRResponse<Data, Error, SWROptions>;
|
||||
}
|
||||
/**
|
||||
* Middleware function type for extending SWR functionality.
|
||||
*
|
||||
* Middleware functions receive the next SWR hook in the chain and return
|
||||
* a modified hook function. This allows for composition of multiple
|
||||
* middleware functions to add features like logging, caching, or
|
||||
* request/response transformation.
|
||||
*
|
||||
* The middleware guarantees that a SWRHook receives a key, fetcher,
|
||||
* and config as arguments, providing a consistent interface for
|
||||
* middleware authors.
|
||||
*
|
||||
* @param useSWRNext - The next SWR hook function in the middleware chain
|
||||
* @returns A new SWR hook function with middleware functionality applied
|
||||
*
|
||||
* @template Data - The type of data returned by the fetcher
|
||||
* @template Error - The type of error that can be thrown
|
||||
*
|
||||
* @public
|
||||
* @see {@link https://swr.vercel.app/docs/middleware | Middleware Documentation}
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const logger: Middleware = (useSWRNext) => (key, fetcher, config) => {
|
||||
* console.log('SWR Request:', key)
|
||||
* return useSWRNext(key, fetcher, config)
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
type Middleware = (useSWRNext: SWRHook) => <Data = any, Error = any>(key: Key, fetcher: BareFetcher<Data> | null, config: SWRConfiguration<Data, Error, BareFetcher<Data>>) => SWRResponse<Data, Error>;
|
||||
/**
|
||||
* Represents a tuple of arguments that can be passed to a fetcher.
|
||||
*
|
||||
* The first element is typically the primary key (like a URL), followed
|
||||
* by additional parameters that affect the request (like query parameters,
|
||||
* headers, or request options).
|
||||
*
|
||||
*/
|
||||
type ArgumentsTuple = readonly [any, ...unknown[]];
|
||||
/**
|
||||
* Valid types for SWR keys.
|
||||
*
|
||||
* SWR keys identify unique requests and can be:
|
||||
* - `string`: Simple URL or identifier
|
||||
* - `ArgumentsTuple`: Array with URL and additional parameters
|
||||
* - `Record<any, any>`: Object that will be serialized
|
||||
* - `null | undefined | false`: Falsy values disable the request
|
||||
*
|
||||
* When a key is falsy, SWR will not make the request, allowing for
|
||||
* conditional fetching based on application state.
|
||||
*
|
||||
* @public
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // String key
|
||||
* useSWR('/api/users', fetcher)
|
||||
*
|
||||
* // Array key with parameters
|
||||
* useSWR(['/api/user', userId], ([url, id]) => fetcher(`${url}/${id}`))
|
||||
*
|
||||
* // Object key
|
||||
* useSWR({ url: '/api/data', params: { page: 1 } }, fetcher)
|
||||
*
|
||||
* // Conditional key
|
||||
* useSWR(userId ? `/api/user/${userId}` : null, fetcher)
|
||||
* ```
|
||||
*/
|
||||
type Arguments = string | ArgumentsTuple | Record<any, any> | null | undefined | false;
|
||||
/**
|
||||
* SWR key that can be static or a function that returns arguments.
|
||||
*
|
||||
* When a function is provided, it's called on each render to determine
|
||||
* the current key. This allows for dynamic keys based on component state
|
||||
* or props.
|
||||
*
|
||||
* @public
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // Static key
|
||||
* useSWR('/api/data', fetcher)
|
||||
*
|
||||
* // Dynamic key function
|
||||
* useSWR(() => user ? `/api/user/${user.id}` : null, fetcher)
|
||||
* ```
|
||||
*/
|
||||
type Key = Arguments | (() => Arguments);
|
||||
/**
|
||||
* Strict tuple key type that only allows tuples or falsy values.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
type StrictTupleKey = ArgumentsTuple | null | undefined | false;
|
||||
/**
|
||||
* Strict key type for internal use.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
type StrictKey = StrictTupleKey | (() => StrictTupleKey);
|
||||
/**
|
||||
* Callback function type for mutator operations.
|
||||
*
|
||||
* This function receives the current cached data and can return new data
|
||||
* to update the cache. It can be synchronous or asynchronous, and can
|
||||
* return undefined to indicate no change should be made.
|
||||
*
|
||||
* @template Data - The type of the cached data
|
||||
* @param currentData - The current data in the cache (may be undefined)
|
||||
* @returns New data to set, undefined for no change, or a Promise resolving to either
|
||||
*
|
||||
* @public
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // Increment a counter
|
||||
* mutate(key, (current: number = 0) => current + 1)
|
||||
*
|
||||
* // Async update
|
||||
* mutate(key, async (current) => {
|
||||
* const updated = await updateData(current)
|
||||
* return updated
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
type MutatorCallback<Data = any> = (currentData?: Data) => Promise<undefined | Data> | undefined | Data;
|
||||
/**
|
||||
* Options for configuring mutator behavior.
|
||||
*
|
||||
* These options control how the mutation affects the cache, revalidation,
|
||||
* and error handling behavior.
|
||||
*
|
||||
* @template Data - The type of the data related to the key
|
||||
* @template MutationData - The type of the data returned by the mutator
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
type MutatorOptions<Data = any, MutationData = Data> = {
|
||||
/**
|
||||
* Whether to revalidate the cache after mutation.
|
||||
*
|
||||
* Can be a boolean or a function that receives the new data and key
|
||||
* to determine whether revalidation should occur.
|
||||
*
|
||||
* @defaultValue true
|
||||
*/
|
||||
revalidate?: boolean | ((data: Data, key: Arguments) => boolean);
|
||||
/**
|
||||
* Whether and how to populate the cache with mutation results.
|
||||
*
|
||||
* - `false`: Don't update the cache
|
||||
* - `true`: Update cache with mutation result directly
|
||||
* - Function: Transform mutation result before caching
|
||||
*
|
||||
* @defaultValue true
|
||||
*/
|
||||
populateCache?: boolean | ((result: MutationData, currentData: Data | undefined) => Data);
|
||||
/**
|
||||
* Optimistic data to show immediately while mutation is pending.
|
||||
*
|
||||
* Can be the data directly or a function that computes it based on
|
||||
* current and displayed data. Useful for immediate UI feedback.
|
||||
*
|
||||
* @defaultValue undefined
|
||||
*/
|
||||
optimisticData?: Data | ((currentData: Data | undefined, displayedData: Data | undefined) => Data);
|
||||
/**
|
||||
* Whether to rollback optimistic updates on error.
|
||||
*
|
||||
* Can be a boolean or a function that receives the error to determine
|
||||
* whether rollback should occur.
|
||||
*
|
||||
* @defaultValue true
|
||||
*/
|
||||
rollbackOnError?: boolean | ((error: unknown) => boolean);
|
||||
/**
|
||||
* Whether to throw errors instead of returning them in the error field.
|
||||
*
|
||||
* When true, errors will be thrown and can be caught with try/catch.
|
||||
* When false, errors are returned in the response object.
|
||||
*
|
||||
* @defaultValue false
|
||||
*/
|
||||
throwOnError?: boolean;
|
||||
};
|
||||
type MutatorConfig = {
|
||||
revalidate?: boolean;
|
||||
populateCache?: boolean;
|
||||
};
|
||||
type Broadcaster<Data = any, Error = any> = (cache: Cache<Data>, key: string, data: Data, error?: Error, isValidating?: boolean, revalidate?: boolean, populateCache?: boolean) => Promise<Data>;
|
||||
/**
|
||||
* Internal state structure stored in the cache.
|
||||
*
|
||||
* This represents the complete state for a cache entry, including
|
||||
* data, error, and loading states. All fields are optional as they
|
||||
* may not be present depending on the request lifecycle.
|
||||
*
|
||||
* @template Data - The type of data stored
|
||||
* @template Error - The type of error that can occur
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
type State<Data = any, Error = any> = {
|
||||
/** The cached data, if available */
|
||||
data?: Data;
|
||||
/** The error object, if an error occurred */
|
||||
error?: Error;
|
||||
/** Whether a revalidation is currently in progress */
|
||||
isValidating?: boolean;
|
||||
/** Whether this is the initial load with no cached data */
|
||||
isLoading?: boolean;
|
||||
};
|
||||
type MutatorFn<Data = any> = (cache: Cache, key: Key, data?: Data | Promise<Data> | MutatorCallback<Data>, opts?: boolean | MutatorOptions<Data>) => Promise<Data | undefined>;
|
||||
type MutatorWrapper<Fn> = Fn extends (...args: [...infer Parameters]) => infer Result ? Parameters[3] extends boolean ? Result : Parameters[3] extends Required<Pick<MutatorOptions, 'populateCache'>> ? Parameters[3]['populateCache'] extends false ? never : Result : Result : never;
|
||||
type Mutator<Data = any> = MutatorWrapper<MutatorFn<Data>>;
|
||||
interface ScopedMutator {
|
||||
/**
|
||||
* @typeParam Data - The type of the data related to the key
|
||||
* @typeParam MutationData - The type of the data returned by the mutator
|
||||
*/
|
||||
<Data = any, MutationData = Data>(matcher: (key?: Arguments) => boolean, data?: MutationData | Promise<MutationData> | MutatorCallback<MutationData>, opts?: boolean | MutatorOptions<Data, MutationData>): Promise<Array<MutationData | undefined>>;
|
||||
/**
|
||||
* @typeParam Data - The type of the data related to the key
|
||||
* @typeParam MutationData - The type of the data returned by the mutator
|
||||
*/
|
||||
<Data = any, T = Data>(key: Arguments, data?: T | Promise<T> | MutatorCallback<T>, opts?: boolean | MutatorOptions<Data, T>): Promise<T | undefined>;
|
||||
}
|
||||
/**
|
||||
* @typeParam Data - The type of the data related to the key
|
||||
* @typeParam MutationData - The type of the data returned by the mutator
|
||||
*/
|
||||
type KeyedMutator<Data> = <MutationData = Data>(data?: Data | Promise<Data | undefined> | MutatorCallback<Data>, opts?: boolean | MutatorOptions<Data, MutationData>) => Promise<Data | MutationData | undefined>;
|
||||
type SWRConfiguration<Data = any, Error = any, Fn extends BareFetcher<any> = BareFetcher<any>> = Partial<PublicConfiguration<Data, Error, Fn>> & Partial<ProviderConfiguration> & {
|
||||
provider?: (cache: Readonly<Cache>) => Cache;
|
||||
cacheData?: never;
|
||||
};
|
||||
type SWRConfigValue<Data = any, Error = any, Fn extends BareFetcher<any> = BareFetcher<any>> = Omit<SWRConfiguration<Data, Error, Fn>, 'cacheData'> & {
|
||||
/**
|
||||
* server-loaded data to be consumed by client hooks and written into cache
|
||||
* @experimental
|
||||
*/
|
||||
cacheData?: CacheData<Data>;
|
||||
};
|
||||
type IsLoadingResponse<Data = any, Options = SWRDefaultOptions<Data>> = SWRGlobalConfig extends {
|
||||
suspense: true;
|
||||
} ? Options extends {
|
||||
suspense: true;
|
||||
} ? false : false : boolean;
|
||||
type SWRDefaultOptions<Data> = SWRConfiguration<Data, Error, Fetcher<Data, Key>>;
|
||||
type SWRConfigurationWithOptionalFallback<Options> = Options extends SWRConfiguration & Required<Pick<SWRConfiguration, 'fallbackData'>> ? Omit<Options, 'fallbackData'> & Pick<Partial<Options>, 'fallbackData'> : Options;
|
||||
/**
|
||||
* The response object returned by SWR hooks.
|
||||
*
|
||||
* This interface represents the return value of useSWR and related hooks,
|
||||
* providing access to data, error state, and control functions.
|
||||
*
|
||||
* @template Data - The type of data returned by the fetcher
|
||||
* @template Error - The type of error that can be thrown
|
||||
* @template Config - The configuration type used to determine blocking behavior
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
interface SWRResponse<Data = any, Error = any, Config = any> {
|
||||
/**
|
||||
* The data returned by the fetcher function.
|
||||
*
|
||||
* - When suspense is enabled or fallbackData is provided: always defined
|
||||
* - Otherwise: `undefined` during initial load or when key is falsy
|
||||
*/
|
||||
data: BlockingData<Data, Config> extends true ? Data : Data | undefined;
|
||||
/**
|
||||
* The error object thrown by the fetcher function.
|
||||
*
|
||||
* `undefined` when there's no error or when a request is in progress.
|
||||
*/
|
||||
error: Error | undefined;
|
||||
/**
|
||||
* Function to mutate the cached data for this specific key.
|
||||
*
|
||||
* This is a bound version of the global mutate function that automatically
|
||||
* uses the current key, providing type safety and convenience.
|
||||
*/
|
||||
mutate: KeyedMutator<Data>;
|
||||
/**
|
||||
* Whether the request is currently being validated (loading fresh data).
|
||||
*
|
||||
* `true` during initial load, revalidation, or when mutate is called
|
||||
* with a promise or async function.
|
||||
*/
|
||||
isValidating: boolean;
|
||||
/**
|
||||
* Whether the request is in initial loading state.
|
||||
*
|
||||
* `true` only during the initial load when there's no cached data.
|
||||
* Unlike `isValidating`, this becomes `false` once data is available.
|
||||
*/
|
||||
isLoading: IsLoadingResponse<Data, Config>;
|
||||
}
|
||||
type KeyLoader<Args extends Arguments = Arguments> = ((index: number, previousPageData: any | null) => Args) | null;
|
||||
interface RevalidatorOptions {
|
||||
retryCount?: number;
|
||||
dedupe?: boolean;
|
||||
}
|
||||
type Revalidator = (revalidateOpts?: RevalidatorOptions) => Promise<boolean> | void;
|
||||
type RevalidateEvent = typeof FOCUS_EVENT | typeof RECONNECT_EVENT | typeof MUTATE_EVENT | typeof ERROR_REVALIDATE_EVENT | typeof UNLOAD_EVENT;
|
||||
type RevalidateCallbackReturnType = {
|
||||
[FOCUS_EVENT]: void;
|
||||
[RECONNECT_EVENT]: void;
|
||||
[MUTATE_EVENT]: Promise<boolean>;
|
||||
[ERROR_REVALIDATE_EVENT]: void;
|
||||
[UNLOAD_EVENT]: void;
|
||||
};
|
||||
type RevalidateCallback = <K extends RevalidateEvent>(type: K, opts?: any) => RevalidateCallbackReturnType[K];
|
||||
/**
|
||||
* Cache interface for storing SWR state.
|
||||
*
|
||||
* This interface defines the contract for cache providers used by SWR.
|
||||
* The default implementation uses a Map, but custom cache providers
|
||||
* can implement this interface to provide different storage mechanisms.
|
||||
*
|
||||
* @template Data - The type of data stored in the cache
|
||||
*
|
||||
* @public
|
||||
* @see {@link https://swr.vercel.app/docs/advanced/cache | Cache Documentation}
|
||||
*/
|
||||
interface Cache<Data = any> {
|
||||
/**
|
||||
* Get an iterator of all cache keys.
|
||||
*
|
||||
* @returns Iterator that yields all cache keys as strings
|
||||
*/
|
||||
keys(): IterableIterator<string>;
|
||||
/**
|
||||
* Get the cached state for a key.
|
||||
*
|
||||
* @param key - The cache key to look up
|
||||
* @returns The cached state or undefined if not found
|
||||
*/
|
||||
get(key: string): State<Data> | undefined;
|
||||
/**
|
||||
* Set the cached state for a key.
|
||||
*
|
||||
* @param key - The cache key to set
|
||||
* @param value - The state to cache
|
||||
*/
|
||||
set(key: string, value: State<Data>): void;
|
||||
/**
|
||||
* Delete a cached entry.
|
||||
*
|
||||
* @param key - The cache key to delete
|
||||
*/
|
||||
delete(key: string): void;
|
||||
}
|
||||
interface StateDependencies {
|
||||
data?: boolean;
|
||||
error?: boolean;
|
||||
isValidating?: boolean;
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
export { cache as E, compare as H, defaultConfig as J, mutate as L, preload as N, unload as O, useSWRConfig as Q, SWRConfig$1 as S, SWRConfig as T, useSWR as W, unstable_serialize as X };
|
||||
export type { Arguments as A, BareFetcher as B, CacheData as C, UnloadOptions as D, FetcherResponse as F, GlobalState as G, InternalConfiguration as I, Key as K, MutatorCallback as M, ProviderConfiguration as P, RevalidateCallback as R, Unloader as U, SWRGlobalConfig as V, Cache as a, ScopedMutator as b, State as c, FullConfiguration as d, MutatorOptions as e, Fetcher as f, SWRConfiguration as g, SWRHook as h, Middleware as i, BlockingData as j, Broadcaster as k, IsLoadingResponse as l, KeyLoader as m, KeyedMutator as n, Mutator as o, MutatorConfig as p, MutatorFn as q, MutatorWrapper as r, PublicConfiguration as s, RevalidateEvent as t, Revalidator as u, RevalidatorOptions as v, SWRConfigValue as w, SWRResponse as x, StateDependencies as y, StrictTupleKey as z };
|
||||
+730
@@ -0,0 +1,730 @@
|
||||
import React, { useRef, useMemo, useCallback, useDebugValue } from 'react';
|
||||
import { useSyncExternalStore } from 'use-sync-external-store/shim';
|
||||
import { withArgs, subscribeCallback } from './_internal/index.mjs';
|
||||
import { U as UNDEFINED, O as OBJECT, S as SWRConfig$1, d as defaultConfig, a as SWRGlobalState, s as serialize, c as createCacheHelper, i as isUndefined, b as isPromiseLike, I as IS_SERVER, n as noop, g as getTimestamp, e as isFunction, E as ERROR_REVALIDATE_EVENT, f as internalMutate, u as useIsomorphicLayoutEffect, r as rAF, F as FOCUS_EVENT, R as RECONNECT_EVENT, M as MUTATE_EVENT, h as UNLOAD_EVENT, j as IS_REACT_LEGACY, m as mergeObjects } from './config-context-ext53wcz.mjs';
|
||||
|
||||
/// <reference types="react/experimental" />
|
||||
const use = React.use || // This extra generic is to avoid TypeScript mixing up the generic and JSX sytax
|
||||
// and emitting an error.
|
||||
// We assume that this is only for the `use(thenable)` case, not `use(context)`.
|
||||
// https://github.com/facebook/react/blob/aed00dacfb79d17c53218404c52b1c7aa59c4a89/packages/react-server/src/ReactFizzThenable.js#L45
|
||||
((thenable)=>{
|
||||
switch(thenable.status){
|
||||
case 'pending':
|
||||
throw thenable;
|
||||
case 'fulfilled':
|
||||
return thenable.value;
|
||||
case 'rejected':
|
||||
throw thenable.reason;
|
||||
default:
|
||||
thenable.status = 'pending';
|
||||
thenable.then((v)=>{
|
||||
thenable.status = 'fulfilled';
|
||||
thenable.value = v;
|
||||
}, (e)=>{
|
||||
thenable.status = 'rejected';
|
||||
thenable.reason = e;
|
||||
});
|
||||
throw thenable;
|
||||
}
|
||||
});
|
||||
const WITH_DEDUPE = {
|
||||
dedupe: true
|
||||
};
|
||||
const isCacheDataConsumed = (consumedCacheData, cacheData, key)=>{
|
||||
var _consumedCacheData_get;
|
||||
return cacheData ? ((_consumedCacheData_get = consumedCacheData.get(cacheData)) == null ? void 0 : _consumedCacheData_get.has(key)) === true : false;
|
||||
};
|
||||
const markCacheDataConsumed = (consumedCacheData, cacheData, key)=>{
|
||||
if (!cacheData) return;
|
||||
let consumedKeys = consumedCacheData.get(cacheData);
|
||||
if (!consumedKeys) {
|
||||
consumedKeys = new Set();
|
||||
consumedCacheData.set(cacheData, consumedKeys);
|
||||
}
|
||||
consumedKeys.add(key);
|
||||
};
|
||||
const commitCacheData = ({ value, getCacheData, canCommit, setCache })=>{
|
||||
const commit = (state)=>{
|
||||
if (canCommit() && isUndefined(getCacheData())) {
|
||||
setCache(state);
|
||||
}
|
||||
};
|
||||
Promise.resolve(value).then((data)=>{
|
||||
commit({
|
||||
data,
|
||||
error: UNDEFINED
|
||||
});
|
||||
}, (error)=>{
|
||||
commit({
|
||||
error
|
||||
});
|
||||
});
|
||||
};
|
||||
// React can only unwrap a thenable synchronously when it has React's thenable
|
||||
// status fields, so this fulfilled no-op keeps Suspense paths from waiting.
|
||||
const resolvedUndef = Promise.resolve(UNDEFINED);
|
||||
// @ts-ignore modify react promise status
|
||||
resolvedUndef.status = 'fulfilled';
|
||||
// @ts-ignore modify react promise value
|
||||
resolvedUndef.value = UNDEFINED;
|
||||
const sub = ()=>noop;
|
||||
/**
|
||||
* The core implementation of the useSWR hook.
|
||||
*
|
||||
* This is the main handler function that implements all SWR functionality including
|
||||
* data fetching, caching, revalidation, error handling, and state management.
|
||||
* It manages the complete lifecycle of SWR requests from initialization through
|
||||
* cleanup.
|
||||
*
|
||||
* Key responsibilities:
|
||||
* - Key serialization and normalization
|
||||
* - Cache state management and synchronization
|
||||
* - Automatic and manual revalidation
|
||||
* - Error handling and retry logic
|
||||
* - Suspense integration
|
||||
* - Loading state management
|
||||
* - Effect cleanup and memory management
|
||||
*
|
||||
* @template Data - The type of data returned by the fetcher
|
||||
* @template Error - The type of error that can be thrown
|
||||
*
|
||||
* @param _key - The SWR key (string, array, object, function, or falsy)
|
||||
* @param fetcher - The fetcher function to retrieve data, or null to disable fetching
|
||||
* @param config - Complete SWR configuration object with both public and internal options
|
||||
*
|
||||
* @returns SWRResponse object containing data, error, mutate function, and loading states
|
||||
*
|
||||
* @internal This is the internal implementation. Use `useSWR` instead.
|
||||
*/ const useSWRHandler = (_key, fetcher, config)=>{
|
||||
const { cache, compare, suspense, fallbackData, revalidateOnMount, revalidateIfStale, refreshInterval, refreshWhenHidden, refreshWhenOffline, keepPreviousData, strictServerPrefetchWarning } = config;
|
||||
const [EVENT_REVALIDATORS, MUTATION, FETCH, PRELOAD] = SWRGlobalState.get(cache);
|
||||
// `key` is the identifier of the SWR internal state,
|
||||
// `fnArg` is the argument/arguments parsed from the key, which will be passed
|
||||
// to the fetcher.
|
||||
// All of them are derived from `_key`.
|
||||
const [key, fnArg] = serialize(_key);
|
||||
// If it's the initial render of this hook.
|
||||
const initialMountedRef = useRef(false);
|
||||
// If the hook is unmounted already. This will be used to prevent some effects
|
||||
// to be called after unmounting.
|
||||
const unmountedRef = useRef(false);
|
||||
// Refs to keep the key and config.
|
||||
const keyRef = useRef(key);
|
||||
const fetcherRef = useRef(fetcher);
|
||||
const configRef = useRef(config);
|
||||
const getConfig = ()=>configRef.current;
|
||||
const isActive = ()=>getConfig().isVisible() && getConfig().isOnline();
|
||||
const [getCache, setCache, subscribeCache, getInitialCache] = createCacheHelper(cache, key);
|
||||
const stateDependencies = useRef({}).current;
|
||||
// Resolve the fallback data from either the inline option, or the global provider.
|
||||
// If it's a promise, we simply let React suspend and resolve it for us.
|
||||
const fallback = isUndefined(fallbackData) ? isUndefined(config.fallback) ? UNDEFINED : config.fallback[key] : fallbackData;
|
||||
const serverCacheData = config.cacheData;
|
||||
const configCacheData = !key ? UNDEFINED : serverCacheData == null ? void 0 : serverCacheData[key];
|
||||
const req = key ? PRELOAD[key] : UNDEFINED;
|
||||
// `cacheData` is request-scoped data provided by a Server Component through
|
||||
// `SWRConfig`'s context. It's only used when there's no in-flight client
|
||||
// `preload()` response (`req`) for the same key.
|
||||
const hasCacheData = isUndefined(req) && !isUndefined(configCacheData);
|
||||
const preloadedData = hasCacheData ? configCacheData : req;
|
||||
const isEqual = (prev, current)=>{
|
||||
for(const _ in stateDependencies){
|
||||
const t = _;
|
||||
if (t === 'data') {
|
||||
if (!compare(prev[t], current[t])) {
|
||||
if (!isUndefined(prev[t])) {
|
||||
return false;
|
||||
}
|
||||
if (!compare(returnedData, current[t])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (current[t] !== prev[t]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
const isInitialMount = !initialMountedRef.current;
|
||||
const getSnapshot = useMemo(()=>{
|
||||
const cachedData = getCache();
|
||||
const initialData = getInitialCache();
|
||||
const getSelectedCache = (state)=>{
|
||||
// We only select the needed fields from the state.
|
||||
const snapshot = mergeObjects(state);
|
||||
delete snapshot._k;
|
||||
const shouldStartRequest = (()=>{
|
||||
if (!key) return false;
|
||||
if (!fetcher) return false;
|
||||
// If it's paused, we skip revalidation.
|
||||
if (getConfig().isPaused()) return false;
|
||||
// If `revalidateOnMount` is set, we take the value directly.
|
||||
if (isInitialMount && !isUndefined(revalidateOnMount)) return revalidateOnMount;
|
||||
const data = !isUndefined(fallback) ? fallback : snapshot.data;
|
||||
// cacheData is consumed during the initial Suspense render. Once data
|
||||
// is cached, fall through to the normal mount revalidation policy.
|
||||
if (suspense && hasCacheData && isUndefined(data)) return false;
|
||||
if (suspense) return isUndefined(data) || revalidateIfStale;
|
||||
return isUndefined(data) || revalidateIfStale;
|
||||
})();
|
||||
if (!shouldStartRequest) {
|
||||
return snapshot;
|
||||
}
|
||||
return {
|
||||
isValidating: true,
|
||||
isLoading: true,
|
||||
...snapshot
|
||||
};
|
||||
};
|
||||
const clientSnapshot = getSelectedCache(cachedData);
|
||||
const serverSnapshot = cachedData === initialData ? clientSnapshot : getSelectedCache(initialData);
|
||||
// To make sure that we are returning the same object reference to avoid
|
||||
// unnecessary re-renders, we keep the previous snapshot and use deep
|
||||
// comparison to check if we need to return a new one.
|
||||
let memorizedSnapshot = clientSnapshot;
|
||||
return [
|
||||
()=>{
|
||||
const newSnapshot = getSelectedCache(getCache());
|
||||
const compareResult = isEqual(newSnapshot, memorizedSnapshot);
|
||||
if (compareResult) {
|
||||
// Mentally, we should always return the `memorizedSnapshot` here
|
||||
// as there's no change between the new and old snapshots.
|
||||
// However, since the `isEqual` function only compares selected fields,
|
||||
// the values of the unselected fields might be changed. That's
|
||||
// simply because we didn't track them.
|
||||
// To support the case in https://github.com/vercel/swr/pull/2576,
|
||||
// we need to update these fields in the `memorizedSnapshot` too
|
||||
// with direct mutations to ensure the snapshot is always up-to-date
|
||||
// even for the unselected fields, but only trigger re-renders when
|
||||
// the selected fields are changed.
|
||||
memorizedSnapshot.data = newSnapshot.data;
|
||||
memorizedSnapshot.isLoading = newSnapshot.isLoading;
|
||||
memorizedSnapshot.isValidating = newSnapshot.isValidating;
|
||||
memorizedSnapshot.error = newSnapshot.error;
|
||||
return memorizedSnapshot;
|
||||
} else {
|
||||
memorizedSnapshot = newSnapshot;
|
||||
return newSnapshot;
|
||||
}
|
||||
},
|
||||
()=>serverSnapshot
|
||||
];
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [
|
||||
cache,
|
||||
key
|
||||
]);
|
||||
// Get the current state that SWR should return.
|
||||
const cached = useSyncExternalStore(useCallback((callback)=>subscribeCache(key, (current, prev)=>{
|
||||
if (!isEqual(prev, current)) callback();
|
||||
}), // eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[
|
||||
cache,
|
||||
key
|
||||
]), getSnapshot[0], getSnapshot[1]);
|
||||
const hasRevalidator = EVENT_REVALIDATORS[key] && EVENT_REVALIDATORS[key].length > 0;
|
||||
const cachedData = cached.data;
|
||||
let data = isUndefined(cachedData) ? fallback && isPromiseLike(fallback) ? use(fallback) : fallback : cachedData;
|
||||
const error = cached.error;
|
||||
// Use a ref to store previously returned data. Use the initial data as its initial value.
|
||||
const laggyDataRef = useRef(data);
|
||||
const consumedCacheDataRef = useRef(UNDEFINED);
|
||||
let consumedCacheData = consumedCacheDataRef.current;
|
||||
if (!consumedCacheData) {
|
||||
consumedCacheData = new WeakMap();
|
||||
consumedCacheDataRef.current = consumedCacheData;
|
||||
}
|
||||
const preloadCacheRef = useRef(null);
|
||||
let returnedData = keepPreviousData ? isUndefined(cachedData) ? isUndefined(laggyDataRef.current) ? data : laggyDataRef.current : cachedData : data;
|
||||
const hasKeyButNoData = key && isUndefined(data);
|
||||
const hydrationRef = useRef(null);
|
||||
// Note: the conditionally hook call is fine because the environment
|
||||
// `IS_SERVER` never changes.
|
||||
// @ts-expect-error -- use hydrationRef directly
|
||||
!IS_SERVER && // getServerSnapshot is only called during hydration
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
useSyncExternalStore(sub, ()=>{
|
||||
hydrationRef.current = false;
|
||||
return hydrationRef;
|
||||
}, ()=>{
|
||||
hydrationRef.current = true;
|
||||
return hydrationRef;
|
||||
});
|
||||
const isHydration = hydrationRef.current;
|
||||
// During the initial SSR render, warn if the key has no data pre-fetched via:
|
||||
// - fallback data
|
||||
// - preload calls
|
||||
// - initial data from the cache provider
|
||||
// We only warn once for each key during Hydration.
|
||||
if (strictServerPrefetchWarning && isHydration && !suspense && hasKeyButNoData) {
|
||||
console.warn(`Missing pre-initiated data for serialized key "${key}" during server-side rendering. Data fetching should be initiated on the server and provided to SWR via fallback data. You can set "strictServerPrefetchWarning: false" to disable this warning.`);
|
||||
}
|
||||
// Resolve the default validating state:
|
||||
// If it's able to validate, and it should revalidate when mount, this will be true.
|
||||
// - Suspense mode and there's stale data for the initial render.
|
||||
// - Not suspense mode and there is no fallback data and `revalidateIfStale` is enabled.
|
||||
// - `revalidateIfStale` is enabled but `data` is not defined.
|
||||
const shouldDoInitialRevalidation = (()=>{
|
||||
if (!key || !fetcher) return false;
|
||||
// If it's paused, we skip revalidation.
|
||||
if (getConfig().isPaused()) return false;
|
||||
// if a key already has revalidators and also has error, we should not trigger revalidation
|
||||
if (hasRevalidator && !isUndefined(error)) return false;
|
||||
// If `revalidateOnMount` is set, we take the value directly.
|
||||
if (isInitialMount && !isUndefined(revalidateOnMount)) return revalidateOnMount;
|
||||
// cacheData satisfies the initial Suspense render without a duplicate
|
||||
// request. Cached data on a later mount follows revalidateIfStale.
|
||||
if (suspense && hasCacheData && hasKeyButNoData) return false;
|
||||
// Under suspense mode, it will always fetch on render if there is no
|
||||
// stale data so no need to revalidate immediately mount it again.
|
||||
// If data exists, only revalidate if `revalidateIfStale` is true.
|
||||
if (suspense) return isUndefined(data) ? false : revalidateIfStale;
|
||||
// If there is no stale data, we need to revalidate when mount;
|
||||
// If `revalidateIfStale` is set to true, we will always revalidate.
|
||||
return isUndefined(data) || revalidateIfStale;
|
||||
})();
|
||||
const defaultValidatingState = isInitialMount && shouldDoInitialRevalidation;
|
||||
const isValidating = isUndefined(cached.isValidating) ? defaultValidatingState : cached.isValidating;
|
||||
const isLoading = isUndefined(cached.isLoading) ? defaultValidatingState : cached.isLoading;
|
||||
// The revalidation function is a carefully crafted wrapper of the original
|
||||
// `fetcher`, to correctly handle the many edge cases.
|
||||
const revalidate = useCallback(async (revalidateOpts)=>{
|
||||
const currentFetcher = fetcherRef.current;
|
||||
if (!key || !currentFetcher || unmountedRef.current || getConfig().isPaused()) {
|
||||
return false;
|
||||
}
|
||||
let newData;
|
||||
let startAt;
|
||||
let loading = true;
|
||||
const opts = revalidateOpts || {};
|
||||
// If there is no ongoing concurrent request, or `dedupe` is not set, a
|
||||
// new request should be initiated.
|
||||
const shouldStartNewRequest = !FETCH[key] || !opts.dedupe;
|
||||
const shouldUseRSCPreload = hasCacheData && !isCacheDataConsumed(consumedCacheData, serverCacheData, key) && !isUndefined(preloadedData) && isUndefined(getCache().data);
|
||||
/*
|
||||
For React 17
|
||||
Do unmount check for calls:
|
||||
If key has changed during the revalidation, or the component has been
|
||||
unmounted, old dispatch and old event callbacks should not take any
|
||||
effect
|
||||
|
||||
For React 18
|
||||
only check if key has changed
|
||||
https://github.com/reactwg/react-18/discussions/82
|
||||
*/ const callbackSafeguard = ()=>{
|
||||
if (IS_REACT_LEGACY) {
|
||||
return !unmountedRef.current && key === keyRef.current && initialMountedRef.current;
|
||||
}
|
||||
return key === keyRef.current;
|
||||
};
|
||||
// The final state object when the request finishes.
|
||||
const finalState = {
|
||||
isValidating: false,
|
||||
isLoading: false
|
||||
};
|
||||
const finishRequestAndUpdateState = ()=>{
|
||||
setCache(finalState);
|
||||
};
|
||||
const cleanupState = ()=>{
|
||||
// Check if it's still the same request before deleting it.
|
||||
const requestInfo = FETCH[key];
|
||||
if (requestInfo && requestInfo[1] === startAt) {
|
||||
delete FETCH[key];
|
||||
}
|
||||
};
|
||||
// Start fetching. Change the `isValidating` state, update the cache.
|
||||
const initialState = {
|
||||
isValidating: true
|
||||
};
|
||||
// It is in the `isLoading` state, if and only if there is no cached data.
|
||||
// This bypasses fallback data and laggy data.
|
||||
if (isUndefined(getCache().data)) {
|
||||
initialState.isLoading = true;
|
||||
}
|
||||
try {
|
||||
if (shouldStartNewRequest) {
|
||||
setCache(initialState);
|
||||
// If no cache is being rendered currently (it shows a blank page),
|
||||
// we trigger the loading slow event.
|
||||
if (config.loadingTimeout && isUndefined(getCache().data)) {
|
||||
setTimeout(()=>{
|
||||
if (loading && callbackSafeguard()) {
|
||||
getConfig().onLoadingSlow(key, config);
|
||||
}
|
||||
}, config.loadingTimeout);
|
||||
}
|
||||
// Start the request and save the timestamp.
|
||||
// Key must be truthy if entering here.
|
||||
if (shouldUseRSCPreload) {
|
||||
markCacheDataConsumed(consumedCacheData, serverCacheData, key);
|
||||
}
|
||||
FETCH[key] = [
|
||||
shouldUseRSCPreload ? preloadedData : currentFetcher(fnArg),
|
||||
getTimestamp()
|
||||
];
|
||||
if (shouldUseRSCPreload && PRELOAD[key]) {
|
||||
delete PRELOAD[key];
|
||||
}
|
||||
}
|
||||
// Wait until the ongoing request is done. Deduplication is also
|
||||
// considered here.
|
||||
;
|
||||
[newData, startAt] = FETCH[key];
|
||||
newData = await newData;
|
||||
if (shouldStartNewRequest) {
|
||||
// If the request isn't interrupted, clean it up after the
|
||||
// deduplication interval.
|
||||
setTimeout(cleanupState, config.dedupingInterval);
|
||||
}
|
||||
// If there're other ongoing request(s), started after the current one,
|
||||
// we need to ignore the current one to avoid possible race conditions:
|
||||
// req1------------------>res1 (current one)
|
||||
// req2---------------->res2
|
||||
// the request that fired later will always be kept.
|
||||
// The timestamp maybe be `undefined` or a number
|
||||
if (!FETCH[key] || FETCH[key][1] !== startAt) {
|
||||
if (shouldStartNewRequest) {
|
||||
if (callbackSafeguard()) {
|
||||
getConfig().onDiscarded(key);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// Clear error.
|
||||
finalState.error = UNDEFINED;
|
||||
// If there're other mutations(s), that overlapped with the current revalidation:
|
||||
// case 1:
|
||||
// req------------------>res
|
||||
// mutate------>end
|
||||
// case 2:
|
||||
// req------------>res
|
||||
// mutate------>end
|
||||
// case 3:
|
||||
// req------------------>res
|
||||
// mutate-------...---------->
|
||||
// we have to ignore the revalidation result (res) because it's no longer fresh.
|
||||
// meanwhile, a new revalidation should be triggered when the mutation ends.
|
||||
const mutationInfo = MUTATION[key];
|
||||
if (!isUndefined(mutationInfo) && // case 1
|
||||
(startAt <= mutationInfo[0] || // case 2
|
||||
startAt <= mutationInfo[1] || // case 3
|
||||
mutationInfo[1] === 0)) {
|
||||
finishRequestAndUpdateState();
|
||||
if (shouldStartNewRequest) {
|
||||
if (callbackSafeguard()) {
|
||||
getConfig().onDiscarded(key);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// Deep compare with the latest state to avoid extra re-renders.
|
||||
// For local state, compare and assign.
|
||||
const cacheData = getCache().data;
|
||||
// Since the compare fn could be custom fn
|
||||
// cacheData might be different from newData even when compare fn returns True
|
||||
finalState.data = compare(cacheData, newData) ? cacheData : newData;
|
||||
// Trigger the successful callback if it's the original request.
|
||||
if (shouldStartNewRequest) {
|
||||
if (callbackSafeguard()) {
|
||||
getConfig().onSuccess(newData, key, config);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
cleanupState();
|
||||
const currentConfig = getConfig();
|
||||
const { shouldRetryOnError } = currentConfig;
|
||||
// Not paused, we continue handling the error. Otherwise, discard it.
|
||||
if (!currentConfig.isPaused()) {
|
||||
// Get a new error, don't use deep comparison for errors.
|
||||
finalState.error = err;
|
||||
// Error event and retry logic. Only for the actual request, not
|
||||
// deduped ones.
|
||||
if (shouldStartNewRequest && callbackSafeguard()) {
|
||||
currentConfig.onError(err, key, currentConfig);
|
||||
if (shouldRetryOnError === true || isFunction(shouldRetryOnError) && shouldRetryOnError(err)) {
|
||||
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.
|
||||
currentConfig.onErrorRetry(err, key, currentConfig, (_opts)=>{
|
||||
const revalidators = EVENT_REVALIDATORS[key];
|
||||
if (revalidators && revalidators[0]) {
|
||||
revalidators[0](ERROR_REVALIDATE_EVENT, _opts);
|
||||
}
|
||||
}, {
|
||||
retryCount: (opts.retryCount || 0) + 1,
|
||||
dedupe: true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Mark loading as stopped.
|
||||
loading = false;
|
||||
// Update the current hook's state.
|
||||
finishRequestAndUpdateState();
|
||||
return true;
|
||||
}, // `setState` is immutable, and `eventsCallback`, `fnArg`, and
|
||||
// `keyValidating` are depending on `key`, so we can exclude them from
|
||||
// the deps array.
|
||||
//
|
||||
// FIXME:
|
||||
// `fn` and `config` might be changed during the lifecycle,
|
||||
// but they might be changed every render like this.
|
||||
// `useSWR('key', () => fetch('/api/'), { suspense: true })`
|
||||
// So we omit the values from the deps array
|
||||
// even though it might cause unexpected behaviors.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[
|
||||
key,
|
||||
cache
|
||||
]);
|
||||
// Similar to the global mutate but bound to the current cache and key.
|
||||
// `cache` isn't allowed to change during the lifecycle.
|
||||
const boundMutate = useCallback(// Use callback to make sure `keyRef.current` returns latest result every time
|
||||
(...args)=>{
|
||||
return internalMutate(cache, keyRef.current, ...args);
|
||||
}, // eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[]);
|
||||
useIsomorphicLayoutEffect(()=>{
|
||||
const preloaded = preloadCacheRef.current;
|
||||
if (!preloaded) return;
|
||||
preloadCacheRef.current = null;
|
||||
markCacheDataConsumed(consumedCacheData, preloaded.cacheData, preloaded.key);
|
||||
if (isUndefined(getCache().data)) {
|
||||
setCache({
|
||||
data: preloaded.data,
|
||||
error: UNDEFINED,
|
||||
_k: preloaded._k
|
||||
});
|
||||
}
|
||||
if (PRELOAD[preloaded.key]) {
|
||||
delete PRELOAD[preloaded.key];
|
||||
}
|
||||
});
|
||||
// The logic for updating refs.
|
||||
useIsomorphicLayoutEffect(()=>{
|
||||
fetcherRef.current = fetcher;
|
||||
configRef.current = config;
|
||||
// Handle laggy data updates. If there's cached data of the current key,
|
||||
// it'll be the correct reference.
|
||||
if (!isUndefined(cachedData)) {
|
||||
laggyDataRef.current = cachedData;
|
||||
}
|
||||
});
|
||||
useIsomorphicLayoutEffect(()=>{
|
||||
if (fetcher || !hasCacheData || isUndefined(preloadedData) || isCacheDataConsumed(consumedCacheData, serverCacheData, key) || !isUndefined(getCache().data)) {
|
||||
return;
|
||||
}
|
||||
markCacheDataConsumed(consumedCacheData, serverCacheData, key);
|
||||
const mutation = MUTATION[key];
|
||||
commitCacheData({
|
||||
value: preloadedData,
|
||||
getCacheData: ()=>getCache().data,
|
||||
canCommit: ()=>!unmountedRef.current && key === keyRef.current && MUTATION[key] === mutation,
|
||||
setCache: (state)=>setCache({
|
||||
...state,
|
||||
_k: fnArg
|
||||
})
|
||||
});
|
||||
});
|
||||
// After mounted or key changed.
|
||||
useIsomorphicLayoutEffect(()=>{
|
||||
if (!key) return;
|
||||
const softRevalidate = revalidate.bind(UNDEFINED, WITH_DEDUPE);
|
||||
let nextFocusRevalidatedAt = 0;
|
||||
if (getConfig().revalidateOnFocus) {
|
||||
const initNow = Date.now();
|
||||
nextFocusRevalidatedAt = initNow + getConfig().focusThrottleInterval;
|
||||
}
|
||||
// Expose revalidators to global event listeners. So we can trigger
|
||||
// revalidation from the outside.
|
||||
const onRevalidate = (type, opts = {})=>{
|
||||
if (type == FOCUS_EVENT) {
|
||||
const now = Date.now();
|
||||
if (getConfig().revalidateOnFocus && now > nextFocusRevalidatedAt && isActive()) {
|
||||
nextFocusRevalidatedAt = now + getConfig().focusThrottleInterval;
|
||||
softRevalidate();
|
||||
}
|
||||
} else if (type == RECONNECT_EVENT) {
|
||||
if (getConfig().revalidateOnReconnect && isActive()) {
|
||||
softRevalidate();
|
||||
}
|
||||
} else if (type == MUTATE_EVENT) {
|
||||
return revalidate();
|
||||
} else if (type == ERROR_REVALIDATE_EVENT) {
|
||||
return revalidate(opts);
|
||||
} else if (type == UNLOAD_EVENT) {
|
||||
// The cache has been cleared: drop the previous data kept by
|
||||
// `keepPreviousData` so it can't stay on screen.
|
||||
laggyDataRef.current = UNDEFINED;
|
||||
if (opts.revalidate) {
|
||||
return revalidate();
|
||||
}
|
||||
}
|
||||
return;
|
||||
};
|
||||
const unsubEvents = subscribeCallback(key, EVENT_REVALIDATORS, onRevalidate);
|
||||
// Mark the component as mounted and update corresponding refs.
|
||||
unmountedRef.current = false;
|
||||
keyRef.current = key;
|
||||
initialMountedRef.current = true;
|
||||
// Keep the original key in the cache.
|
||||
setCache({
|
||||
_k: fnArg
|
||||
});
|
||||
// Trigger a revalidation
|
||||
if (shouldDoInitialRevalidation) {
|
||||
// Performance optimization: if a request is already in progress for this key,
|
||||
// skip the revalidation to avoid redundant work
|
||||
if (!FETCH[key]) {
|
||||
if (isUndefined(data) || IS_SERVER) {
|
||||
// Revalidate immediately.
|
||||
softRevalidate();
|
||||
} else {
|
||||
// Delay the revalidate if we have data to return so we won't block
|
||||
// rendering.
|
||||
rAF(softRevalidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ()=>{
|
||||
// Mark it as unmounted.
|
||||
unmountedRef.current = true;
|
||||
unsubEvents();
|
||||
};
|
||||
}, [
|
||||
key
|
||||
]);
|
||||
// Polling
|
||||
useIsomorphicLayoutEffect(()=>{
|
||||
let timer;
|
||||
function next() {
|
||||
// Use the passed interval
|
||||
// ...or invoke the function with the updated data to get the interval
|
||||
const interval = isFunction(refreshInterval) ? refreshInterval(getCache().data) : refreshInterval;
|
||||
// We only start the next interval if `refreshInterval` is not 0, and:
|
||||
// - `force` is true, which is the start of polling
|
||||
// - or `timer` is not 0, which means the effect wasn't canceled
|
||||
if (interval && timer !== -1) {
|
||||
timer = setTimeout(execute, interval);
|
||||
}
|
||||
}
|
||||
function execute() {
|
||||
// Check if it's OK to execute:
|
||||
// Only revalidate when the page is visible, online, and not errored.
|
||||
if (!getCache().error && (refreshWhenHidden || getConfig().isVisible()) && (refreshWhenOffline || getConfig().isOnline())) {
|
||||
revalidate(WITH_DEDUPE).then(next);
|
||||
} else {
|
||||
// Schedule the next interval to check again.
|
||||
next();
|
||||
}
|
||||
}
|
||||
next();
|
||||
return ()=>{
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
timer = -1;
|
||||
}
|
||||
};
|
||||
}, [
|
||||
refreshInterval,
|
||||
refreshWhenHidden,
|
||||
refreshWhenOffline,
|
||||
key
|
||||
]);
|
||||
// Display debug info in React DevTools.
|
||||
useDebugValue(returnedData);
|
||||
// In Suspense mode, we can't return the empty `data` state.
|
||||
// If there is an `error`, the `error` needs to be thrown to the error boundary.
|
||||
// If there is no `error`, the `revalidation` promise needs to be thrown to
|
||||
// the suspense boundary.
|
||||
if (suspense) {
|
||||
// SWR should throw when trying to use Suspense on the server with React 18,
|
||||
// without providing any fallback data. This causes hydration errors. See:
|
||||
// https://github.com/vercel/swr/issues/1832
|
||||
if (!IS_REACT_LEGACY && IS_SERVER && hasKeyButNoData && isUndefined(preloadedData)) {
|
||||
throw new Error('Fallback data is required when using Suspense in SSR.');
|
||||
}
|
||||
// Always update fetcher and config refs even with the Suspense mode.
|
||||
if (hasKeyButNoData) {
|
||||
fetcherRef.current = fetcher;
|
||||
configRef.current = config;
|
||||
unmountedRef.current = false;
|
||||
}
|
||||
const shouldConsumePreload = !isUndefined(preloadedData) && hasKeyButNoData;
|
||||
let preloadData = UNDEFINED;
|
||||
if (shouldConsumePreload && hasCacheData) {
|
||||
preloadData = preloadedData && isPromiseLike(preloadedData) ? use(preloadedData) : preloadedData;
|
||||
data = preloadData;
|
||||
returnedData = preloadData;
|
||||
if (!IS_SERVER) {
|
||||
preloadCacheRef.current = {
|
||||
data: preloadData,
|
||||
_k: fnArg,
|
||||
key,
|
||||
cacheData: serverCacheData
|
||||
};
|
||||
}
|
||||
} else {
|
||||
const mutateReq = shouldConsumePreload ? boundMutate(preloadedData) : resolvedUndef;
|
||||
use(mutateReq);
|
||||
}
|
||||
if (!isUndefined(error) && hasKeyButNoData) {
|
||||
throw error;
|
||||
}
|
||||
const revalidation = hasKeyButNoData && isUndefined(preloadData) ? revalidate(WITH_DEDUPE) : resolvedUndef;
|
||||
if (!isUndefined(returnedData) && hasKeyButNoData) {
|
||||
// @ts-ignore modify react promise status
|
||||
revalidation.status = 'fulfilled';
|
||||
// @ts-ignore modify react promise value
|
||||
revalidation.value = true;
|
||||
}
|
||||
use(revalidation);
|
||||
}
|
||||
const swrResponse = {
|
||||
mutate: boundMutate,
|
||||
get data () {
|
||||
stateDependencies.data = true;
|
||||
return returnedData;
|
||||
},
|
||||
get error () {
|
||||
stateDependencies.error = true;
|
||||
return error;
|
||||
},
|
||||
get isValidating () {
|
||||
stateDependencies.isValidating = true;
|
||||
return isValidating;
|
||||
},
|
||||
get isLoading () {
|
||||
stateDependencies.isLoading = true;
|
||||
return isLoading;
|
||||
}
|
||||
};
|
||||
return swrResponse;
|
||||
};
|
||||
const SWRConfig = OBJECT.defineProperty(SWRConfig$1, 'defaultValue', {
|
||||
value: defaultConfig
|
||||
});
|
||||
/**
|
||||
* A hook to fetch data.
|
||||
*
|
||||
* @see {@link https://swr.vercel.app}
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
* import useSWR from 'swr'
|
||||
* function Profile() {
|
||||
* const { data, error, isLoading } = useSWR('/api/user', fetcher)
|
||||
* if (error) return <div>failed to load</div>
|
||||
* if (isLoading) return <div>loading...</div>
|
||||
* return <div>hello {data.name}!</div>
|
||||
* }
|
||||
* ```
|
||||
*/ const useSWR = withArgs(useSWRHandler);
|
||||
|
||||
export { SWRConfig as S, useSWR as u };
|
||||
+735
@@ -0,0 +1,735 @@
|
||||
var React = require('react');
|
||||
var shim = require('use-sync-external-store/shim');
|
||||
var _internal_index = require('./_internal/index.js');
|
||||
var bunchee_group_0 = require('./config-context-s960zqob.js');
|
||||
|
||||
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
||||
|
||||
var React__default = /*#__PURE__*/_interopDefault(React);
|
||||
|
||||
/// <reference types="react/experimental" />
|
||||
const use = React__default.default.use || // This extra generic is to avoid TypeScript mixing up the generic and JSX sytax
|
||||
// and emitting an error.
|
||||
// We assume that this is only for the `use(thenable)` case, not `use(context)`.
|
||||
// https://github.com/facebook/react/blob/aed00dacfb79d17c53218404c52b1c7aa59c4a89/packages/react-server/src/ReactFizzThenable.js#L45
|
||||
((thenable)=>{
|
||||
switch(thenable.status){
|
||||
case 'pending':
|
||||
throw thenable;
|
||||
case 'fulfilled':
|
||||
return thenable.value;
|
||||
case 'rejected':
|
||||
throw thenable.reason;
|
||||
default:
|
||||
thenable.status = 'pending';
|
||||
thenable.then((v)=>{
|
||||
thenable.status = 'fulfilled';
|
||||
thenable.value = v;
|
||||
}, (e)=>{
|
||||
thenable.status = 'rejected';
|
||||
thenable.reason = e;
|
||||
});
|
||||
throw thenable;
|
||||
}
|
||||
});
|
||||
const WITH_DEDUPE = {
|
||||
dedupe: true
|
||||
};
|
||||
const isCacheDataConsumed = (consumedCacheData, cacheData, key)=>{
|
||||
var _consumedCacheData_get;
|
||||
return cacheData ? ((_consumedCacheData_get = consumedCacheData.get(cacheData)) == null ? void 0 : _consumedCacheData_get.has(key)) === true : false;
|
||||
};
|
||||
const markCacheDataConsumed = (consumedCacheData, cacheData, key)=>{
|
||||
if (!cacheData) return;
|
||||
let consumedKeys = consumedCacheData.get(cacheData);
|
||||
if (!consumedKeys) {
|
||||
consumedKeys = new Set();
|
||||
consumedCacheData.set(cacheData, consumedKeys);
|
||||
}
|
||||
consumedKeys.add(key);
|
||||
};
|
||||
const commitCacheData = ({ value, getCacheData, canCommit, setCache })=>{
|
||||
const commit = (state)=>{
|
||||
if (canCommit() && bunchee_group_0.isUndefined(getCacheData())) {
|
||||
setCache(state);
|
||||
}
|
||||
};
|
||||
Promise.resolve(value).then((data)=>{
|
||||
commit({
|
||||
data,
|
||||
error: bunchee_group_0.UNDEFINED
|
||||
});
|
||||
}, (error)=>{
|
||||
commit({
|
||||
error
|
||||
});
|
||||
});
|
||||
};
|
||||
// React can only unwrap a thenable synchronously when it has React's thenable
|
||||
// status fields, so this fulfilled no-op keeps Suspense paths from waiting.
|
||||
const resolvedUndef = Promise.resolve(bunchee_group_0.UNDEFINED);
|
||||
// @ts-ignore modify react promise status
|
||||
resolvedUndef.status = 'fulfilled';
|
||||
// @ts-ignore modify react promise value
|
||||
resolvedUndef.value = bunchee_group_0.UNDEFINED;
|
||||
const sub = ()=>bunchee_group_0.noop;
|
||||
/**
|
||||
* The core implementation of the useSWR hook.
|
||||
*
|
||||
* This is the main handler function that implements all SWR functionality including
|
||||
* data fetching, caching, revalidation, error handling, and state management.
|
||||
* It manages the complete lifecycle of SWR requests from initialization through
|
||||
* cleanup.
|
||||
*
|
||||
* Key responsibilities:
|
||||
* - Key serialization and normalization
|
||||
* - Cache state management and synchronization
|
||||
* - Automatic and manual revalidation
|
||||
* - Error handling and retry logic
|
||||
* - Suspense integration
|
||||
* - Loading state management
|
||||
* - Effect cleanup and memory management
|
||||
*
|
||||
* @template Data - The type of data returned by the fetcher
|
||||
* @template Error - The type of error that can be thrown
|
||||
*
|
||||
* @param _key - The SWR key (string, array, object, function, or falsy)
|
||||
* @param fetcher - The fetcher function to retrieve data, or null to disable fetching
|
||||
* @param config - Complete SWR configuration object with both public and internal options
|
||||
*
|
||||
* @returns SWRResponse object containing data, error, mutate function, and loading states
|
||||
*
|
||||
* @internal This is the internal implementation. Use `useSWR` instead.
|
||||
*/ const useSWRHandler = (_key, fetcher, config)=>{
|
||||
const { cache, compare, suspense, fallbackData, revalidateOnMount, revalidateIfStale, refreshInterval, refreshWhenHidden, refreshWhenOffline, keepPreviousData, strictServerPrefetchWarning } = config;
|
||||
const [EVENT_REVALIDATORS, MUTATION, FETCH, PRELOAD] = bunchee_group_0.SWRGlobalState.get(cache);
|
||||
// `key` is the identifier of the SWR internal state,
|
||||
// `fnArg` is the argument/arguments parsed from the key, which will be passed
|
||||
// to the fetcher.
|
||||
// All of them are derived from `_key`.
|
||||
const [key, fnArg] = bunchee_group_0.serialize(_key);
|
||||
// If it's the initial render of this hook.
|
||||
const initialMountedRef = React.useRef(false);
|
||||
// If the hook is unmounted already. This will be used to prevent some effects
|
||||
// to be called after unmounting.
|
||||
const unmountedRef = React.useRef(false);
|
||||
// Refs to keep the key and config.
|
||||
const keyRef = React.useRef(key);
|
||||
const fetcherRef = React.useRef(fetcher);
|
||||
const configRef = React.useRef(config);
|
||||
const getConfig = ()=>configRef.current;
|
||||
const isActive = ()=>getConfig().isVisible() && getConfig().isOnline();
|
||||
const [getCache, setCache, subscribeCache, getInitialCache] = bunchee_group_0.createCacheHelper(cache, key);
|
||||
const stateDependencies = React.useRef({}).current;
|
||||
// Resolve the fallback data from either the inline option, or the global provider.
|
||||
// If it's a promise, we simply let React suspend and resolve it for us.
|
||||
const fallback = bunchee_group_0.isUndefined(fallbackData) ? bunchee_group_0.isUndefined(config.fallback) ? bunchee_group_0.UNDEFINED : config.fallback[key] : fallbackData;
|
||||
const serverCacheData = config.cacheData;
|
||||
const configCacheData = !key ? bunchee_group_0.UNDEFINED : serverCacheData == null ? void 0 : serverCacheData[key];
|
||||
const req = key ? PRELOAD[key] : bunchee_group_0.UNDEFINED;
|
||||
// `cacheData` is request-scoped data provided by a Server Component through
|
||||
// `SWRConfig`'s context. It's only used when there's no in-flight client
|
||||
// `preload()` response (`req`) for the same key.
|
||||
const hasCacheData = bunchee_group_0.isUndefined(req) && !bunchee_group_0.isUndefined(configCacheData);
|
||||
const preloadedData = hasCacheData ? configCacheData : req;
|
||||
const isEqual = (prev, current)=>{
|
||||
for(const _ in stateDependencies){
|
||||
const t = _;
|
||||
if (t === 'data') {
|
||||
if (!compare(prev[t], current[t])) {
|
||||
if (!bunchee_group_0.isUndefined(prev[t])) {
|
||||
return false;
|
||||
}
|
||||
if (!compare(returnedData, current[t])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (current[t] !== prev[t]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
const isInitialMount = !initialMountedRef.current;
|
||||
const getSnapshot = React.useMemo(()=>{
|
||||
const cachedData = getCache();
|
||||
const initialData = getInitialCache();
|
||||
const getSelectedCache = (state)=>{
|
||||
// We only select the needed fields from the state.
|
||||
const snapshot = bunchee_group_0.mergeObjects(state);
|
||||
delete snapshot._k;
|
||||
const shouldStartRequest = (()=>{
|
||||
if (!key) return false;
|
||||
if (!fetcher) return false;
|
||||
// If it's paused, we skip revalidation.
|
||||
if (getConfig().isPaused()) return false;
|
||||
// If `revalidateOnMount` is set, we take the value directly.
|
||||
if (isInitialMount && !bunchee_group_0.isUndefined(revalidateOnMount)) return revalidateOnMount;
|
||||
const data = !bunchee_group_0.isUndefined(fallback) ? fallback : snapshot.data;
|
||||
// cacheData is consumed during the initial Suspense render. Once data
|
||||
// is cached, fall through to the normal mount revalidation policy.
|
||||
if (suspense && hasCacheData && bunchee_group_0.isUndefined(data)) return false;
|
||||
if (suspense) return bunchee_group_0.isUndefined(data) || revalidateIfStale;
|
||||
return bunchee_group_0.isUndefined(data) || revalidateIfStale;
|
||||
})();
|
||||
if (!shouldStartRequest) {
|
||||
return snapshot;
|
||||
}
|
||||
return {
|
||||
isValidating: true,
|
||||
isLoading: true,
|
||||
...snapshot
|
||||
};
|
||||
};
|
||||
const clientSnapshot = getSelectedCache(cachedData);
|
||||
const serverSnapshot = cachedData === initialData ? clientSnapshot : getSelectedCache(initialData);
|
||||
// To make sure that we are returning the same object reference to avoid
|
||||
// unnecessary re-renders, we keep the previous snapshot and use deep
|
||||
// comparison to check if we need to return a new one.
|
||||
let memorizedSnapshot = clientSnapshot;
|
||||
return [
|
||||
()=>{
|
||||
const newSnapshot = getSelectedCache(getCache());
|
||||
const compareResult = isEqual(newSnapshot, memorizedSnapshot);
|
||||
if (compareResult) {
|
||||
// Mentally, we should always return the `memorizedSnapshot` here
|
||||
// as there's no change between the new and old snapshots.
|
||||
// However, since the `isEqual` function only compares selected fields,
|
||||
// the values of the unselected fields might be changed. That's
|
||||
// simply because we didn't track them.
|
||||
// To support the case in https://github.com/vercel/swr/pull/2576,
|
||||
// we need to update these fields in the `memorizedSnapshot` too
|
||||
// with direct mutations to ensure the snapshot is always up-to-date
|
||||
// even for the unselected fields, but only trigger re-renders when
|
||||
// the selected fields are changed.
|
||||
memorizedSnapshot.data = newSnapshot.data;
|
||||
memorizedSnapshot.isLoading = newSnapshot.isLoading;
|
||||
memorizedSnapshot.isValidating = newSnapshot.isValidating;
|
||||
memorizedSnapshot.error = newSnapshot.error;
|
||||
return memorizedSnapshot;
|
||||
} else {
|
||||
memorizedSnapshot = newSnapshot;
|
||||
return newSnapshot;
|
||||
}
|
||||
},
|
||||
()=>serverSnapshot
|
||||
];
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [
|
||||
cache,
|
||||
key
|
||||
]);
|
||||
// Get the current state that SWR should return.
|
||||
const cached = shim.useSyncExternalStore(React.useCallback((callback)=>subscribeCache(key, (current, prev)=>{
|
||||
if (!isEqual(prev, current)) callback();
|
||||
}), // eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[
|
||||
cache,
|
||||
key
|
||||
]), getSnapshot[0], getSnapshot[1]);
|
||||
const hasRevalidator = EVENT_REVALIDATORS[key] && EVENT_REVALIDATORS[key].length > 0;
|
||||
const cachedData = cached.data;
|
||||
let data = bunchee_group_0.isUndefined(cachedData) ? fallback && bunchee_group_0.isPromiseLike(fallback) ? use(fallback) : fallback : cachedData;
|
||||
const error = cached.error;
|
||||
// Use a ref to store previously returned data. Use the initial data as its initial value.
|
||||
const laggyDataRef = React.useRef(data);
|
||||
const consumedCacheDataRef = React.useRef(bunchee_group_0.UNDEFINED);
|
||||
let consumedCacheData = consumedCacheDataRef.current;
|
||||
if (!consumedCacheData) {
|
||||
consumedCacheData = new WeakMap();
|
||||
consumedCacheDataRef.current = consumedCacheData;
|
||||
}
|
||||
const preloadCacheRef = React.useRef(null);
|
||||
let returnedData = keepPreviousData ? bunchee_group_0.isUndefined(cachedData) ? bunchee_group_0.isUndefined(laggyDataRef.current) ? data : laggyDataRef.current : cachedData : data;
|
||||
const hasKeyButNoData = key && bunchee_group_0.isUndefined(data);
|
||||
const hydrationRef = React.useRef(null);
|
||||
// Note: the conditionally hook call is fine because the environment
|
||||
// `IS_SERVER` never changes.
|
||||
// @ts-expect-error -- use hydrationRef directly
|
||||
!bunchee_group_0.IS_SERVER && // getServerSnapshot is only called during hydration
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
shim.useSyncExternalStore(sub, ()=>{
|
||||
hydrationRef.current = false;
|
||||
return hydrationRef;
|
||||
}, ()=>{
|
||||
hydrationRef.current = true;
|
||||
return hydrationRef;
|
||||
});
|
||||
const isHydration = hydrationRef.current;
|
||||
// During the initial SSR render, warn if the key has no data pre-fetched via:
|
||||
// - fallback data
|
||||
// - preload calls
|
||||
// - initial data from the cache provider
|
||||
// We only warn once for each key during Hydration.
|
||||
if (strictServerPrefetchWarning && isHydration && !suspense && hasKeyButNoData) {
|
||||
console.warn(`Missing pre-initiated data for serialized key "${key}" during server-side rendering. Data fetching should be initiated on the server and provided to SWR via fallback data. You can set "strictServerPrefetchWarning: false" to disable this warning.`);
|
||||
}
|
||||
// Resolve the default validating state:
|
||||
// If it's able to validate, and it should revalidate when mount, this will be true.
|
||||
// - Suspense mode and there's stale data for the initial render.
|
||||
// - Not suspense mode and there is no fallback data and `revalidateIfStale` is enabled.
|
||||
// - `revalidateIfStale` is enabled but `data` is not defined.
|
||||
const shouldDoInitialRevalidation = (()=>{
|
||||
if (!key || !fetcher) return false;
|
||||
// If it's paused, we skip revalidation.
|
||||
if (getConfig().isPaused()) return false;
|
||||
// if a key already has revalidators and also has error, we should not trigger revalidation
|
||||
if (hasRevalidator && !bunchee_group_0.isUndefined(error)) return false;
|
||||
// If `revalidateOnMount` is set, we take the value directly.
|
||||
if (isInitialMount && !bunchee_group_0.isUndefined(revalidateOnMount)) return revalidateOnMount;
|
||||
// cacheData satisfies the initial Suspense render without a duplicate
|
||||
// request. Cached data on a later mount follows revalidateIfStale.
|
||||
if (suspense && hasCacheData && hasKeyButNoData) return false;
|
||||
// Under suspense mode, it will always fetch on render if there is no
|
||||
// stale data so no need to revalidate immediately mount it again.
|
||||
// If data exists, only revalidate if `revalidateIfStale` is true.
|
||||
if (suspense) return bunchee_group_0.isUndefined(data) ? false : revalidateIfStale;
|
||||
// If there is no stale data, we need to revalidate when mount;
|
||||
// If `revalidateIfStale` is set to true, we will always revalidate.
|
||||
return bunchee_group_0.isUndefined(data) || revalidateIfStale;
|
||||
})();
|
||||
const defaultValidatingState = isInitialMount && shouldDoInitialRevalidation;
|
||||
const isValidating = bunchee_group_0.isUndefined(cached.isValidating) ? defaultValidatingState : cached.isValidating;
|
||||
const isLoading = bunchee_group_0.isUndefined(cached.isLoading) ? defaultValidatingState : cached.isLoading;
|
||||
// The revalidation function is a carefully crafted wrapper of the original
|
||||
// `fetcher`, to correctly handle the many edge cases.
|
||||
const revalidate = React.useCallback(async (revalidateOpts)=>{
|
||||
const currentFetcher = fetcherRef.current;
|
||||
if (!key || !currentFetcher || unmountedRef.current || getConfig().isPaused()) {
|
||||
return false;
|
||||
}
|
||||
let newData;
|
||||
let startAt;
|
||||
let loading = true;
|
||||
const opts = revalidateOpts || {};
|
||||
// If there is no ongoing concurrent request, or `dedupe` is not set, a
|
||||
// new request should be initiated.
|
||||
const shouldStartNewRequest = !FETCH[key] || !opts.dedupe;
|
||||
const shouldUseRSCPreload = hasCacheData && !isCacheDataConsumed(consumedCacheData, serverCacheData, key) && !bunchee_group_0.isUndefined(preloadedData) && bunchee_group_0.isUndefined(getCache().data);
|
||||
/*
|
||||
For React 17
|
||||
Do unmount check for calls:
|
||||
If key has changed during the revalidation, or the component has been
|
||||
unmounted, old dispatch and old event callbacks should not take any
|
||||
effect
|
||||
|
||||
For React 18
|
||||
only check if key has changed
|
||||
https://github.com/reactwg/react-18/discussions/82
|
||||
*/ const callbackSafeguard = ()=>{
|
||||
if (bunchee_group_0.IS_REACT_LEGACY) {
|
||||
return !unmountedRef.current && key === keyRef.current && initialMountedRef.current;
|
||||
}
|
||||
return key === keyRef.current;
|
||||
};
|
||||
// The final state object when the request finishes.
|
||||
const finalState = {
|
||||
isValidating: false,
|
||||
isLoading: false
|
||||
};
|
||||
const finishRequestAndUpdateState = ()=>{
|
||||
setCache(finalState);
|
||||
};
|
||||
const cleanupState = ()=>{
|
||||
// Check if it's still the same request before deleting it.
|
||||
const requestInfo = FETCH[key];
|
||||
if (requestInfo && requestInfo[1] === startAt) {
|
||||
delete FETCH[key];
|
||||
}
|
||||
};
|
||||
// Start fetching. Change the `isValidating` state, update the cache.
|
||||
const initialState = {
|
||||
isValidating: true
|
||||
};
|
||||
// It is in the `isLoading` state, if and only if there is no cached data.
|
||||
// This bypasses fallback data and laggy data.
|
||||
if (bunchee_group_0.isUndefined(getCache().data)) {
|
||||
initialState.isLoading = true;
|
||||
}
|
||||
try {
|
||||
if (shouldStartNewRequest) {
|
||||
setCache(initialState);
|
||||
// If no cache is being rendered currently (it shows a blank page),
|
||||
// we trigger the loading slow event.
|
||||
if (config.loadingTimeout && bunchee_group_0.isUndefined(getCache().data)) {
|
||||
setTimeout(()=>{
|
||||
if (loading && callbackSafeguard()) {
|
||||
getConfig().onLoadingSlow(key, config);
|
||||
}
|
||||
}, config.loadingTimeout);
|
||||
}
|
||||
// Start the request and save the timestamp.
|
||||
// Key must be truthy if entering here.
|
||||
if (shouldUseRSCPreload) {
|
||||
markCacheDataConsumed(consumedCacheData, serverCacheData, key);
|
||||
}
|
||||
FETCH[key] = [
|
||||
shouldUseRSCPreload ? preloadedData : currentFetcher(fnArg),
|
||||
bunchee_group_0.getTimestamp()
|
||||
];
|
||||
if (shouldUseRSCPreload && PRELOAD[key]) {
|
||||
delete PRELOAD[key];
|
||||
}
|
||||
}
|
||||
// Wait until the ongoing request is done. Deduplication is also
|
||||
// considered here.
|
||||
;
|
||||
[newData, startAt] = FETCH[key];
|
||||
newData = await newData;
|
||||
if (shouldStartNewRequest) {
|
||||
// If the request isn't interrupted, clean it up after the
|
||||
// deduplication interval.
|
||||
setTimeout(cleanupState, config.dedupingInterval);
|
||||
}
|
||||
// If there're other ongoing request(s), started after the current one,
|
||||
// we need to ignore the current one to avoid possible race conditions:
|
||||
// req1------------------>res1 (current one)
|
||||
// req2---------------->res2
|
||||
// the request that fired later will always be kept.
|
||||
// The timestamp maybe be `undefined` or a number
|
||||
if (!FETCH[key] || FETCH[key][1] !== startAt) {
|
||||
if (shouldStartNewRequest) {
|
||||
if (callbackSafeguard()) {
|
||||
getConfig().onDiscarded(key);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// Clear error.
|
||||
finalState.error = bunchee_group_0.UNDEFINED;
|
||||
// If there're other mutations(s), that overlapped with the current revalidation:
|
||||
// case 1:
|
||||
// req------------------>res
|
||||
// mutate------>end
|
||||
// case 2:
|
||||
// req------------>res
|
||||
// mutate------>end
|
||||
// case 3:
|
||||
// req------------------>res
|
||||
// mutate-------...---------->
|
||||
// we have to ignore the revalidation result (res) because it's no longer fresh.
|
||||
// meanwhile, a new revalidation should be triggered when the mutation ends.
|
||||
const mutationInfo = MUTATION[key];
|
||||
if (!bunchee_group_0.isUndefined(mutationInfo) && // case 1
|
||||
(startAt <= mutationInfo[0] || // case 2
|
||||
startAt <= mutationInfo[1] || // case 3
|
||||
mutationInfo[1] === 0)) {
|
||||
finishRequestAndUpdateState();
|
||||
if (shouldStartNewRequest) {
|
||||
if (callbackSafeguard()) {
|
||||
getConfig().onDiscarded(key);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// Deep compare with the latest state to avoid extra re-renders.
|
||||
// For local state, compare and assign.
|
||||
const cacheData = getCache().data;
|
||||
// Since the compare fn could be custom fn
|
||||
// cacheData might be different from newData even when compare fn returns True
|
||||
finalState.data = compare(cacheData, newData) ? cacheData : newData;
|
||||
// Trigger the successful callback if it's the original request.
|
||||
if (shouldStartNewRequest) {
|
||||
if (callbackSafeguard()) {
|
||||
getConfig().onSuccess(newData, key, config);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
cleanupState();
|
||||
const currentConfig = getConfig();
|
||||
const { shouldRetryOnError } = currentConfig;
|
||||
// Not paused, we continue handling the error. Otherwise, discard it.
|
||||
if (!currentConfig.isPaused()) {
|
||||
// Get a new error, don't use deep comparison for errors.
|
||||
finalState.error = err;
|
||||
// Error event and retry logic. Only for the actual request, not
|
||||
// deduped ones.
|
||||
if (shouldStartNewRequest && callbackSafeguard()) {
|
||||
currentConfig.onError(err, key, currentConfig);
|
||||
if (shouldRetryOnError === true || bunchee_group_0.isFunction(shouldRetryOnError) && shouldRetryOnError(err)) {
|
||||
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.
|
||||
currentConfig.onErrorRetry(err, key, currentConfig, (_opts)=>{
|
||||
const revalidators = EVENT_REVALIDATORS[key];
|
||||
if (revalidators && revalidators[0]) {
|
||||
revalidators[0](bunchee_group_0.ERROR_REVALIDATE_EVENT, _opts);
|
||||
}
|
||||
}, {
|
||||
retryCount: (opts.retryCount || 0) + 1,
|
||||
dedupe: true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Mark loading as stopped.
|
||||
loading = false;
|
||||
// Update the current hook's state.
|
||||
finishRequestAndUpdateState();
|
||||
return true;
|
||||
}, // `setState` is immutable, and `eventsCallback`, `fnArg`, and
|
||||
// `keyValidating` are depending on `key`, so we can exclude them from
|
||||
// the deps array.
|
||||
//
|
||||
// FIXME:
|
||||
// `fn` and `config` might be changed during the lifecycle,
|
||||
// but they might be changed every render like this.
|
||||
// `useSWR('key', () => fetch('/api/'), { suspense: true })`
|
||||
// So we omit the values from the deps array
|
||||
// even though it might cause unexpected behaviors.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[
|
||||
key,
|
||||
cache
|
||||
]);
|
||||
// Similar to the global mutate but bound to the current cache and key.
|
||||
// `cache` isn't allowed to change during the lifecycle.
|
||||
const boundMutate = React.useCallback(// Use callback to make sure `keyRef.current` returns latest result every time
|
||||
(...args)=>{
|
||||
return bunchee_group_0.internalMutate(cache, keyRef.current, ...args);
|
||||
}, // eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[]);
|
||||
bunchee_group_0.useIsomorphicLayoutEffect(()=>{
|
||||
const preloaded = preloadCacheRef.current;
|
||||
if (!preloaded) return;
|
||||
preloadCacheRef.current = null;
|
||||
markCacheDataConsumed(consumedCacheData, preloaded.cacheData, preloaded.key);
|
||||
if (bunchee_group_0.isUndefined(getCache().data)) {
|
||||
setCache({
|
||||
data: preloaded.data,
|
||||
error: bunchee_group_0.UNDEFINED,
|
||||
_k: preloaded._k
|
||||
});
|
||||
}
|
||||
if (PRELOAD[preloaded.key]) {
|
||||
delete PRELOAD[preloaded.key];
|
||||
}
|
||||
});
|
||||
// The logic for updating refs.
|
||||
bunchee_group_0.useIsomorphicLayoutEffect(()=>{
|
||||
fetcherRef.current = fetcher;
|
||||
configRef.current = config;
|
||||
// Handle laggy data updates. If there's cached data of the current key,
|
||||
// it'll be the correct reference.
|
||||
if (!bunchee_group_0.isUndefined(cachedData)) {
|
||||
laggyDataRef.current = cachedData;
|
||||
}
|
||||
});
|
||||
bunchee_group_0.useIsomorphicLayoutEffect(()=>{
|
||||
if (fetcher || !hasCacheData || bunchee_group_0.isUndefined(preloadedData) || isCacheDataConsumed(consumedCacheData, serverCacheData, key) || !bunchee_group_0.isUndefined(getCache().data)) {
|
||||
return;
|
||||
}
|
||||
markCacheDataConsumed(consumedCacheData, serverCacheData, key);
|
||||
const mutation = MUTATION[key];
|
||||
commitCacheData({
|
||||
value: preloadedData,
|
||||
getCacheData: ()=>getCache().data,
|
||||
canCommit: ()=>!unmountedRef.current && key === keyRef.current && MUTATION[key] === mutation,
|
||||
setCache: (state)=>setCache({
|
||||
...state,
|
||||
_k: fnArg
|
||||
})
|
||||
});
|
||||
});
|
||||
// After mounted or key changed.
|
||||
bunchee_group_0.useIsomorphicLayoutEffect(()=>{
|
||||
if (!key) return;
|
||||
const softRevalidate = revalidate.bind(bunchee_group_0.UNDEFINED, WITH_DEDUPE);
|
||||
let nextFocusRevalidatedAt = 0;
|
||||
if (getConfig().revalidateOnFocus) {
|
||||
const initNow = Date.now();
|
||||
nextFocusRevalidatedAt = initNow + getConfig().focusThrottleInterval;
|
||||
}
|
||||
// Expose revalidators to global event listeners. So we can trigger
|
||||
// revalidation from the outside.
|
||||
const onRevalidate = (type, opts = {})=>{
|
||||
if (type == bunchee_group_0.FOCUS_EVENT) {
|
||||
const now = Date.now();
|
||||
if (getConfig().revalidateOnFocus && now > nextFocusRevalidatedAt && isActive()) {
|
||||
nextFocusRevalidatedAt = now + getConfig().focusThrottleInterval;
|
||||
softRevalidate();
|
||||
}
|
||||
} else if (type == bunchee_group_0.RECONNECT_EVENT) {
|
||||
if (getConfig().revalidateOnReconnect && isActive()) {
|
||||
softRevalidate();
|
||||
}
|
||||
} else if (type == bunchee_group_0.MUTATE_EVENT) {
|
||||
return revalidate();
|
||||
} else if (type == bunchee_group_0.ERROR_REVALIDATE_EVENT) {
|
||||
return revalidate(opts);
|
||||
} else if (type == bunchee_group_0.UNLOAD_EVENT) {
|
||||
// The cache has been cleared: drop the previous data kept by
|
||||
// `keepPreviousData` so it can't stay on screen.
|
||||
laggyDataRef.current = bunchee_group_0.UNDEFINED;
|
||||
if (opts.revalidate) {
|
||||
return revalidate();
|
||||
}
|
||||
}
|
||||
return;
|
||||
};
|
||||
const unsubEvents = _internal_index.subscribeCallback(key, EVENT_REVALIDATORS, onRevalidate);
|
||||
// Mark the component as mounted and update corresponding refs.
|
||||
unmountedRef.current = false;
|
||||
keyRef.current = key;
|
||||
initialMountedRef.current = true;
|
||||
// Keep the original key in the cache.
|
||||
setCache({
|
||||
_k: fnArg
|
||||
});
|
||||
// Trigger a revalidation
|
||||
if (shouldDoInitialRevalidation) {
|
||||
// Performance optimization: if a request is already in progress for this key,
|
||||
// skip the revalidation to avoid redundant work
|
||||
if (!FETCH[key]) {
|
||||
if (bunchee_group_0.isUndefined(data) || bunchee_group_0.IS_SERVER) {
|
||||
// Revalidate immediately.
|
||||
softRevalidate();
|
||||
} else {
|
||||
// Delay the revalidate if we have data to return so we won't block
|
||||
// rendering.
|
||||
bunchee_group_0.rAF(softRevalidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ()=>{
|
||||
// Mark it as unmounted.
|
||||
unmountedRef.current = true;
|
||||
unsubEvents();
|
||||
};
|
||||
}, [
|
||||
key
|
||||
]);
|
||||
// Polling
|
||||
bunchee_group_0.useIsomorphicLayoutEffect(()=>{
|
||||
let timer;
|
||||
function next() {
|
||||
// Use the passed interval
|
||||
// ...or invoke the function with the updated data to get the interval
|
||||
const interval = bunchee_group_0.isFunction(refreshInterval) ? refreshInterval(getCache().data) : refreshInterval;
|
||||
// We only start the next interval if `refreshInterval` is not 0, and:
|
||||
// - `force` is true, which is the start of polling
|
||||
// - or `timer` is not 0, which means the effect wasn't canceled
|
||||
if (interval && timer !== -1) {
|
||||
timer = setTimeout(execute, interval);
|
||||
}
|
||||
}
|
||||
function execute() {
|
||||
// Check if it's OK to execute:
|
||||
// Only revalidate when the page is visible, online, and not errored.
|
||||
if (!getCache().error && (refreshWhenHidden || getConfig().isVisible()) && (refreshWhenOffline || getConfig().isOnline())) {
|
||||
revalidate(WITH_DEDUPE).then(next);
|
||||
} else {
|
||||
// Schedule the next interval to check again.
|
||||
next();
|
||||
}
|
||||
}
|
||||
next();
|
||||
return ()=>{
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
timer = -1;
|
||||
}
|
||||
};
|
||||
}, [
|
||||
refreshInterval,
|
||||
refreshWhenHidden,
|
||||
refreshWhenOffline,
|
||||
key
|
||||
]);
|
||||
// Display debug info in React DevTools.
|
||||
React.useDebugValue(returnedData);
|
||||
// In Suspense mode, we can't return the empty `data` state.
|
||||
// If there is an `error`, the `error` needs to be thrown to the error boundary.
|
||||
// If there is no `error`, the `revalidation` promise needs to be thrown to
|
||||
// the suspense boundary.
|
||||
if (suspense) {
|
||||
// SWR should throw when trying to use Suspense on the server with React 18,
|
||||
// without providing any fallback data. This causes hydration errors. See:
|
||||
// https://github.com/vercel/swr/issues/1832
|
||||
if (!bunchee_group_0.IS_REACT_LEGACY && bunchee_group_0.IS_SERVER && hasKeyButNoData && bunchee_group_0.isUndefined(preloadedData)) {
|
||||
throw new Error('Fallback data is required when using Suspense in SSR.');
|
||||
}
|
||||
// Always update fetcher and config refs even with the Suspense mode.
|
||||
if (hasKeyButNoData) {
|
||||
fetcherRef.current = fetcher;
|
||||
configRef.current = config;
|
||||
unmountedRef.current = false;
|
||||
}
|
||||
const shouldConsumePreload = !bunchee_group_0.isUndefined(preloadedData) && hasKeyButNoData;
|
||||
let preloadData = bunchee_group_0.UNDEFINED;
|
||||
if (shouldConsumePreload && hasCacheData) {
|
||||
preloadData = preloadedData && bunchee_group_0.isPromiseLike(preloadedData) ? use(preloadedData) : preloadedData;
|
||||
data = preloadData;
|
||||
returnedData = preloadData;
|
||||
if (!bunchee_group_0.IS_SERVER) {
|
||||
preloadCacheRef.current = {
|
||||
data: preloadData,
|
||||
_k: fnArg,
|
||||
key,
|
||||
cacheData: serverCacheData
|
||||
};
|
||||
}
|
||||
} else {
|
||||
const mutateReq = shouldConsumePreload ? boundMutate(preloadedData) : resolvedUndef;
|
||||
use(mutateReq);
|
||||
}
|
||||
if (!bunchee_group_0.isUndefined(error) && hasKeyButNoData) {
|
||||
throw error;
|
||||
}
|
||||
const revalidation = hasKeyButNoData && bunchee_group_0.isUndefined(preloadData) ? revalidate(WITH_DEDUPE) : resolvedUndef;
|
||||
if (!bunchee_group_0.isUndefined(returnedData) && hasKeyButNoData) {
|
||||
// @ts-ignore modify react promise status
|
||||
revalidation.status = 'fulfilled';
|
||||
// @ts-ignore modify react promise value
|
||||
revalidation.value = true;
|
||||
}
|
||||
use(revalidation);
|
||||
}
|
||||
const swrResponse = {
|
||||
mutate: boundMutate,
|
||||
get data () {
|
||||
stateDependencies.data = true;
|
||||
return returnedData;
|
||||
},
|
||||
get error () {
|
||||
stateDependencies.error = true;
|
||||
return error;
|
||||
},
|
||||
get isValidating () {
|
||||
stateDependencies.isValidating = true;
|
||||
return isValidating;
|
||||
},
|
||||
get isLoading () {
|
||||
stateDependencies.isLoading = true;
|
||||
return isLoading;
|
||||
}
|
||||
};
|
||||
return swrResponse;
|
||||
};
|
||||
const SWRConfig = bunchee_group_0.OBJECT.defineProperty(bunchee_group_0.SWRConfig, 'defaultValue', {
|
||||
value: bunchee_group_0.defaultConfig
|
||||
});
|
||||
/**
|
||||
* A hook to fetch data.
|
||||
*
|
||||
* @see {@link https://swr.vercel.app}
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
* import useSWR from 'swr'
|
||||
* function Profile() {
|
||||
* const { data, error, isLoading } = useSWR('/api/user', fetcher)
|
||||
* if (error) return <div>failed to load</div>
|
||||
* if (isLoading) return <div>loading...</div>
|
||||
* return <div>hello {data.name}!</div>
|
||||
* }
|
||||
* ```
|
||||
*/ const useSWR = _internal_index.withArgs(useSWRHandler);
|
||||
|
||||
exports.SWRConfig = SWRConfig;
|
||||
exports.useSWR = useSWR;
|
||||
Reference in new issue
Block a user