react-query
Version:
Hooks for managing, caching and syncing asynchronous and remote data in React
157 lines (130 loc) • 4.14 kB
JavaScript
import { focusManager } from './focusManager';
import { onlineManager } from './onlineManager';
import { sleep } from './utils';
function defaultRetryDelay(failureCount) {
return Math.min(1000 * 2 ** failureCount, 30000);
}
export function canFetch(networkMode) {
return (networkMode != null ? networkMode : 'online') === 'online' ? onlineManager.isOnline() : true;
}
export class CancelledError {
constructor(options) {
this.revert = options == null ? void 0 : options.revert;
this.silent = options == null ? void 0 : options.silent;
}
}
export function isCancelledError(value) {
return value instanceof CancelledError;
}
export function createRetryer(config) {
let isRetryCancelled = false;
let failureCount = 0;
let isResolved = false;
let continueFn;
let promiseResolve;
let promiseReject;
const promise = new Promise((outerResolve, outerReject) => {
promiseResolve = outerResolve;
promiseReject = outerReject;
});
const cancel = cancelOptions => {
if (!isResolved) {
reject(new CancelledError(cancelOptions));
config.abort == null ? void 0 : config.abort();
}
};
const cancelRetry = () => {
isRetryCancelled = true;
};
const continueRetry = () => {
isRetryCancelled = false;
};
const shouldPause = () => !focusManager.isFocused() || config.networkMode !== 'always' && !onlineManager.isOnline();
const resolve = value => {
if (!isResolved) {
isResolved = true;
config.onSuccess == null ? void 0 : config.onSuccess(value);
continueFn == null ? void 0 : continueFn();
promiseResolve(value);
}
};
const reject = value => {
if (!isResolved) {
isResolved = true;
config.onError == null ? void 0 : config.onError(value);
continueFn == null ? void 0 : continueFn();
promiseReject(value);
}
};
const pause = () => {
return new Promise(continueResolve => {
continueFn = value => {
if (isResolved || !shouldPause()) {
return continueResolve(value);
}
};
config.onPause == null ? void 0 : config.onPause();
}).then(() => {
continueFn = undefined;
if (!isResolved) {
config.onContinue == null ? void 0 : config.onContinue();
}
});
}; // Create loop function
const run = () => {
// Do nothing if already resolved
if (isResolved) {
return;
}
let promiseOrValue; // Execute query
try {
promiseOrValue = config.fn();
} catch (error) {
promiseOrValue = Promise.reject(error);
}
Promise.resolve(promiseOrValue).then(resolve).catch(error => {
var _config$retry, _config$retryDelay;
// Stop if the fetch is already resolved
if (isResolved) {
return;
} // Do we need to retry the request?
const retry = (_config$retry = config.retry) != null ? _config$retry : 3;
const retryDelay = (_config$retryDelay = config.retryDelay) != null ? _config$retryDelay : defaultRetryDelay;
const delay = typeof retryDelay === 'function' ? retryDelay(failureCount, error) : retryDelay;
const shouldRetry = retry === true || typeof retry === 'number' && failureCount < retry || typeof retry === 'function' && retry(failureCount, error);
if (isRetryCancelled || !shouldRetry) {
// We are done if the query does not need to be retried
reject(error);
return;
}
failureCount++; // Notify on fail
config.onFail == null ? void 0 : config.onFail(failureCount, error); // Delay
sleep(delay) // Pause if the document is not visible or when the device is offline
.then(() => {
if (shouldPause()) {
return pause();
}
}).then(() => {
if (isRetryCancelled) {
reject(error);
} else {
run();
}
});
});
}; // Start loop
if (canFetch(config.networkMode)) {
run();
} else {
pause().then(run);
}
return {
promise,
cancel,
continue: () => {
continueFn == null ? void 0 : continueFn();
},
cancelRetry,
continueRetry
};
}