react-query
Version:
Hooks for managing, caching and syncing asynchronous and remote data in React
330 lines (284 loc) • 10.7 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react')) :
typeof define === 'function' && define.amd ? define(['exports', 'react'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ReactQueryPersistQueryClient = {}, global.React));
})(this, (function (exports, React) { 'use strict';
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
// TYPES
// FUNCTIONS
function dehydrateMutation(mutation) {
return {
mutationKey: mutation.options.mutationKey,
state: mutation.state
};
} // Most config is not dehydrated but instead meant to configure again when
// consuming the de/rehydrated data, typically with useQuery on the client.
// Sometimes it might make sense to prefetch data on the server and include
// in the html-payload, but not consume it on the initial render.
function dehydrateQuery(query) {
return {
state: query.state,
queryKey: query.queryKey,
queryHash: query.queryHash
};
}
function defaultShouldDehydrateMutation(mutation) {
return mutation.state.isPaused;
}
function defaultShouldDehydrateQuery(query) {
return query.state.status === 'success';
}
function dehydrate(client, options = {}) {
const mutations = [];
const queries = [];
if (options.dehydrateMutations !== false) {
const shouldDehydrateMutation = options.shouldDehydrateMutation || defaultShouldDehydrateMutation;
client.getMutationCache().getAll().forEach(mutation => {
if (shouldDehydrateMutation(mutation)) {
mutations.push(dehydrateMutation(mutation));
}
});
}
if (options.dehydrateQueries !== false) {
const shouldDehydrateQuery = options.shouldDehydrateQuery || defaultShouldDehydrateQuery;
client.getQueryCache().getAll().forEach(query => {
if (shouldDehydrateQuery(query)) {
queries.push(dehydrateQuery(query));
}
});
}
return {
mutations,
queries
};
}
function hydrate(client, dehydratedState, options) {
if (typeof dehydratedState !== 'object' || dehydratedState === null) {
return;
}
const mutationCache = client.getMutationCache();
const queryCache = client.getQueryCache(); // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
const mutations = dehydratedState.mutations || []; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
const queries = dehydratedState.queries || [];
mutations.forEach(dehydratedMutation => {
var _options$defaultOptio;
mutationCache.build(client, { ...(options == null ? void 0 : (_options$defaultOptio = options.defaultOptions) == null ? void 0 : _options$defaultOptio.mutations),
mutationKey: dehydratedMutation.mutationKey
}, dehydratedMutation.state);
});
queries.forEach(dehydratedQuery => {
var _options$defaultOptio2;
const query = queryCache.get(dehydratedQuery.queryHash); // Do not hydrate if an existing query exists with newer data
if (query) {
if (query.state.dataUpdatedAt < dehydratedQuery.state.dataUpdatedAt) {
query.setState(dehydratedQuery.state);
}
return;
} // Restore query
queryCache.build(client, { ...(options == null ? void 0 : (_options$defaultOptio2 = options.defaultOptions) == null ? void 0 : _options$defaultOptio2.queries),
queryKey: dehydratedQuery.queryKey,
queryHash: dehydratedQuery.queryHash
}, dehydratedQuery.state);
});
}
/**
* Restores persisted data to the QueryCache
* - data obtained from persister.restoreClient
* - data is hydrated using hydrateOptions
* If data is expired, busted, empty, or throws, it runs persister.removeClient
*/
async function persistQueryClientRestore({
queryClient,
persister,
maxAge = 1000 * 60 * 60 * 24,
buster = '',
hydrateOptions
}) {
if (typeof window !== 'undefined') {
try {
const persistedClient = await persister.restoreClient();
if (persistedClient) {
if (persistedClient.timestamp) {
const expired = Date.now() - persistedClient.timestamp > maxAge;
const busted = persistedClient.buster !== buster;
if (expired || busted) {
persister.removeClient();
} else {
hydrate(queryClient, persistedClient.clientState, hydrateOptions);
}
} else {
persister.removeClient();
}
}
} catch (err) {
if (process.env.NODE_ENV !== 'production') {
queryClient.getLogger().error(err);
queryClient.getLogger().warn('Encountered an error attempting to restore client cache from persisted location. As a precaution, the persisted cache will be discarded.');
}
persister.removeClient();
}
}
}
/**
* Persists data from the QueryCache
* - data dehydrated using dehydrateOptions
* - data is persisted using persister.persistClient
*/
async function persistQueryClientSave({
queryClient,
persister,
buster = '',
dehydrateOptions
}) {
if (typeof window !== 'undefined') {
const persistClient = {
buster,
timestamp: Date.now(),
clientState: dehydrate(queryClient, dehydrateOptions)
};
await persister.persistClient(persistClient);
}
}
/**
* Subscribe to QueryCache and MutationCache updates (for persisting)
* @returns an unsubscribe function (to discontinue monitoring)
*/
function persistQueryClientSubscribe(props) {
const unsubscribeQueryCache = props.queryClient.getQueryCache().subscribe(() => {
persistQueryClientSave(props);
});
const unusbscribeMutationCache = props.queryClient.getMutationCache().subscribe(() => {
persistQueryClientSave(props);
});
return () => {
unsubscribeQueryCache();
unusbscribeMutationCache();
};
}
/**
* Restores persisted data to QueryCache and persists further changes.
*/
function persistQueryClient(props) {
let hasUnsubscribed = false;
let persistQueryClientUnsubscribe;
const unsubscribe = () => {
hasUnsubscribed = true;
persistQueryClientUnsubscribe == null ? void 0 : persistQueryClientUnsubscribe();
};
let restorePromise = Promise.resolve();
if (typeof window !== 'undefined') {
// Attempt restore
restorePromise = persistQueryClientRestore(props).then(() => {
if (!hasUnsubscribed) {
// Subscribe to changes in the query cache to trigger the save
persistQueryClientUnsubscribe = persistQueryClientSubscribe(props);
}
});
}
return [unsubscribe, restorePromise];
}
function _extends() {
_extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
const defaultContext = /*#__PURE__*/React__default["default"].createContext(undefined);
const QueryClientSharingContext = /*#__PURE__*/React__default["default"].createContext(false); // If we are given a context, we will use it.
// Otherwise, if contextSharing is on, we share the first and at least one
// instance of the context across the window
// to ensure that if React Query is used across
// different bundles or microfrontends they will
// all use the same **instance** of context, regardless
// of module scoping.
function getQueryClientContext(context, contextSharing) {
if (context) {
return context;
}
if (contextSharing && typeof window !== 'undefined') {
if (!window.ReactQueryClientContext) {
window.ReactQueryClientContext = defaultContext;
}
return window.ReactQueryClientContext;
}
return defaultContext;
}
const QueryClientProvider = ({
client,
children,
context,
contextSharing = false
}) => {
React__default["default"].useEffect(() => {
client.mount();
return () => {
client.unmount();
};
}, [client]);
const Context = getQueryClientContext(context, contextSharing);
return /*#__PURE__*/React__default["default"].createElement(QueryClientSharingContext.Provider, {
value: !context && contextSharing
}, /*#__PURE__*/React__default["default"].createElement(Context.Provider, {
value: client
}, children));
};
const IsRestoringContext = /*#__PURE__*/React__default["default"].createContext(false);
const IsRestoringProvider = IsRestoringContext.Provider;
const PersistQueryClientProvider = ({
client,
children,
persistOptions,
onSuccess,
...props
}) => {
const [isRestoring, setIsRestoring] = React__default["default"].useState(true);
const refs = React__default["default"].useRef({
persistOptions,
onSuccess
});
React__default["default"].useEffect(() => {
refs.current = {
persistOptions,
onSuccess
};
});
React__default["default"].useEffect(() => {
let isStale = false;
setIsRestoring(true);
const [unsubscribe, promise] = persistQueryClient({ ...refs.current.persistOptions,
queryClient: client
});
promise.then(() => {
if (!isStale) {
refs.current.onSuccess == null ? void 0 : refs.current.onSuccess();
setIsRestoring(false);
}
});
return () => {
isStale = true;
unsubscribe();
};
}, [client]);
return /*#__PURE__*/React__default["default"].createElement(QueryClientProvider, _extends({
client: client
}, props), /*#__PURE__*/React__default["default"].createElement(IsRestoringProvider, {
value: isRestoring
}, children));
};
exports.PersistQueryClientProvider = PersistQueryClientProvider;
exports.persistQueryClient = persistQueryClient;
exports.persistQueryClientRestore = persistQueryClientRestore;
exports.persistQueryClientSave = persistQueryClientSave;
exports.persistQueryClientSubscribe = persistQueryClientSubscribe;
Object.defineProperty(exports, '__esModule', { value: true });
}));
//# sourceMappingURL=persistQueryClient.development.js.map