Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@nanostores/query

Package Overview
Dependencies
Maintainers
4
Versions
28
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@nanostores/query - npm Package Compare versions

Comparing version 0.2.10 to 0.3.0

36

dist/main.d.ts

@@ -1,2 +0,3 @@

import { MapStore, ReadableAtom } from "nanostores";
import { MapStore, ReadableAtom } from 'nanostores';
type NoKey = null | undefined | void | false;

@@ -9,10 +10,17 @@ type SomeKey = string | number | true;

export type Fetcher<T> = (...args: KeyParts) => Promise<T>;
export type OnErrorRetry = (opts: {
error: unknown;
key: Key;
retryCount: number;
}) => number | void | false | null | undefined;
type EventTypes = {
onError?: (error: any) => unknown;
onError?: (error: unknown) => void;
};
type RefetchSettings = {
dedupeTime?: number;
refetchOnFocus?: boolean;
refetchOnReconnect?: boolean;
refetchInterval?: number;
revalidateOnFocus?: boolean;
revalidateOnReconnect?: boolean;
revalidateInterval?: number;
cacheLifetime?: number;
onErrorRetry?: OnErrorRetry | null | false;
};

@@ -23,3 +31,9 @@ export type CommonSettings<T = unknown> = {

export type NanoqueryArgs = {
cache?: Map<Key, any>;
cache?: Map<Key, {
data?: unknown;
error?: unknown;
retryCount?: number;
created?: number;
expires?: number;
}>;
} & CommonSettings;

@@ -34,4 +48,5 @@ export type FetcherValue<T = any, E = Error> = {

_: Symbol;
key?: string;
key?: Key;
invalidate: (...args: any[]) => void;
revalidate: (...args: any[]) => void;
mutate: (data?: T) => void;

@@ -43,2 +58,3 @@ };

invalidate: (key: KeySelector) => void;
revalidate: (key: KeySelector) => void;
getCacheUpdater: <T = unknown>(key: Key, shouldRevalidate?: boolean) => [(newValue?: T) => void, T | undefined];

@@ -55,7 +71,11 @@ }) => Promise<Result>;

};
export declare const nanoquery: ({ cache, fetcher: globalFetcher, ...globalSettings }?: NanoqueryArgs) => readonly [<T = unknown, E = any>(keyInput: KeyInput, { fetcher, ...fetcherSettings }?: CommonSettings<T>) => FetcherStore<T, E>, <Data = void, Result = unknown, E_1 = any>(mutator: ManualMutator<Data, Result>) => MutatorStore<Data, Result, E_1>, {
export declare const nanoquery: ({ cache, fetcher: globalFetcher, ...globalSettings }?: NanoqueryArgs) => readonly [<T = unknown, E = any>(keyInput: KeyInput, { fetcher, ...fetcherSettings }?: CommonSettings<T>) => FetcherStore<T, E>, <Data = void, Result = unknown, E_1 = any>(mutator: ManualMutator<Data, Result>, opts?: {
throttleCalls?: boolean;
onError?: EventTypes["onError"];
}) => MutatorStore<Data, Result, E_1>, {
readonly __unsafeOverruleSettings: (data: CommonSettings) => void;
readonly invalidateKeys: (keySelector: KeySelector) => void;
readonly revalidateKeys: (keySelector: KeySelector) => void;
readonly mutateCache: (keySelector: KeySelector, data?: unknown) => void;
}];
export {};

@@ -1,3 +0,4 @@

import { map, onStart, onStop, atom, startTask } from "nanostores";
import { createNanoEvents } from "nanoevents";
import { map, onStart, onStop, atom, batched, startTask } from 'nanostores';
import { createNanoEvents } from 'nanoevents';
const nanoquery = ({

@@ -15,6 +16,12 @@ cache = /* @__PURE__ */ new Map(),

subscribe("online", () => events.emit(RECONNECT));
const _refetchOnInterval = /* @__PURE__ */ new Map(), _lastFetch = /* @__PURE__ */ new Map(), _runningFetches = /* @__PURE__ */ new Map();
const _revalidateOnInterval = /* @__PURE__ */ new Map(), _errorInvalidateTimeouts = /* @__PURE__ */ new Map(), _runningFetches = /* @__PURE__ */ new Map();
let rewrittenSettings = {};
const runFetcher = async ([key, keyParts], store, settings, force) => {
var _a;
const getCachedValueByKey = (key) => {
const fromCache = cache.get(key);
if (!fromCache)
return [];
const cacheHit = (fromCache.expires || 0) > (/* @__PURE__ */ new Date()).getTime();
return cacheHit ? [fromCache.data, fromCache.error] : [];
};
const runFetcher = async ([key, keyParts], store, settings) => {
if (!focus)

@@ -28,5 +35,6 @@ return;

};
const setAsLoading = () => {
const setAsLoading = (prev) => {
const toSet = prev === void 0 ? {} : { data: prev };
set({
...store.value,
...toSet,
...loading,

@@ -36,18 +44,27 @@ promise: _runningFetches.get(key)

};
const { dedupeTime = 4e3, fetcher } = {
let {
dedupeTime = 4e3,
cacheLifetime = Infinity,
fetcher,
onErrorRetry = defaultOnErrorRetry
} = {
...settings,
...rewrittenSettings
};
const now = getNow();
if (cacheLifetime < dedupeTime)
cacheLifetime = dedupeTime;
const now = (/* @__PURE__ */ new Date()).getTime();
if (_runningFetches.has(key)) {
if (!store.value.loading)
setAsLoading();
setAsLoading(getCachedValueByKey(key)[0]);
return;
}
if (!force) {
const cached = cache.get(key);
if (cached && store.value.data !== cached)
set({ data: cached, ...notLoading });
const last = _lastFetch.get(key);
if (last && last + dedupeTime > now) {
let cachedValue, cachedError;
const fromCache = cache.get(key);
if (fromCache?.data !== void 0 || fromCache?.error) {
[cachedValue, cachedError] = getCachedValueByKey(key);
if ((fromCache.created || 0) + dedupeTime > now) {
if (store.value.data != cachedValue || store.value.error != cachedError) {
set({ ...notLoading, data: cachedValue, error: cachedError });
}
return;

@@ -58,12 +75,34 @@ }

try {
clearTimeout(_errorInvalidateTimeouts.get(key));
const promise = fetcher(...keyParts);
_lastFetch.set(key, now);
_runningFetches.set(key, promise);
setAsLoading();
setAsLoading(cachedValue);
const res = await promise;
cache.set(key, res);
cache.set(key, {
data: res,
created: (/* @__PURE__ */ new Date()).getTime(),
expires: (/* @__PURE__ */ new Date()).getTime() + cacheLifetime
});
set({ data: res, ...notLoading });
_lastFetch.set(key, getNow());
} catch (error) {
(_a = settings.onError) == null ? void 0 : _a.call(settings, error);
settings.onError?.(error);
const retryCount = (cache.get(key)?.retryCount || 0) + 1;
cache.set(key, {
error,
created: (/* @__PURE__ */ new Date()).getTime(),
expires: (/* @__PURE__ */ new Date()).getTime() + cacheLifetime,
retryCount
});
if (onErrorRetry) {
const timer = onErrorRetry({
error,
key,
retryCount
});
if (timer)
_errorInvalidateTimeouts.set(
key,
setTimeout(() => invalidateKeys(key), timer)
);
}
set({ data: store.value.data, error, ...notLoading });

@@ -94,2 +133,8 @@ } finally {

};
fetcherStore.revalidate = () => {
const { key } = fetcherStore;
if (key) {
revalidateKeys(key);
}
};
fetcherStore.mutate = (data) => {

@@ -125,5 +170,5 @@ const { key } = fetcherStore;

const {
refetchInterval = 0,
refetchOnFocus,
refetchOnReconnect
revalidateInterval = 0,
revalidateOnFocus,
revalidateOnReconnect
} = settings;

@@ -134,18 +179,20 @@ const runRefetcher = () => {

};
if (refetchInterval > 0) {
_refetchOnInterval.set(
if (revalidateInterval > 0) {
_revalidateOnInterval.set(
keyInput,
setInterval(runRefetcher, refetchInterval)
setInterval(runRefetcher, revalidateInterval)
);
}
if (refetchOnFocus)
if (revalidateOnFocus)
evtUnsubs.push(events.on(FOCUS, runRefetcher));
if (refetchOnReconnect)
if (revalidateOnReconnect)
evtUnsubs.push(events.on(RECONNECT, runRefetcher));
const cacheKeyChangeHandler = (keySelector) => {
if (prevKey && testKeyAgainstSelector(prevKey, keySelector)) {
runFetcher([prevKey, prevKeyParts], fetcherStore, settings);
}
};
evtUnsubs.push(
events.on(INVALIDATE_KEYS, (keySelector) => {
if (prevKey && testKeyAgainstSelector(prevKey, keySelector)) {
runFetcher([prevKey, prevKeyParts], fetcherStore, settings, true);
}
}),
events.on(INVALIDATE_KEYS, cacheKeyChangeHandler),
events.on(REVALIDATE_KEYS, cacheKeyChangeHandler),
events.on(SET_CACHE, (keySelector, data, full) => {

@@ -173,14 +220,10 @@ if (prevKey && testKeyAgainstSelector(prevKey, keySelector) && fetcherStore.value !== data && fetcherStore.value.data !== data) {

fetcherStore.value = { ...notLoading };
keysInternalUnsub == null ? void 0 : keysInternalUnsub();
keysInternalUnsub?.();
evtUnsubs.forEach((fn) => fn());
evtUnsubs = [];
keyUnsub == null ? void 0 : keyUnsub();
clearInterval(_refetchOnInterval.get(keyInput));
keyUnsub?.();
clearInterval(_revalidateOnInterval.get(keyInput));
});
return fetcherStore;
};
const nukeKey = (key) => {
cache.delete(key);
_lastFetch.delete(key);
};
const iterOverCache = (keySelector, cb) => {

@@ -193,19 +236,45 @@ for (const key of cache.keys()) {

const invalidateKeys = (keySelector) => {
iterOverCache(keySelector, nukeKey);
iterOverCache(keySelector, (key) => {
cache.delete(key);
});
events.emit(INVALIDATE_KEYS, keySelector);
};
const revalidateKeys = (keySelector) => {
iterOverCache(keySelector, (key) => {
const cached = cache.get(key);
if (cached) {
cache.set(key, { ...cached, created: -Infinity });
}
});
events.emit(REVALIDATE_KEYS, keySelector);
};
const mutateCache = (keySelector, data) => {
iterOverCache(keySelector, (key) => {
if (data === void 0)
nukeKey(key);
else
cache.set(key, data);
cache.delete(key);
else {
cache.set(key, {
data,
created: (/* @__PURE__ */ new Date()).getTime(),
expires: (/* @__PURE__ */ new Date()).getTime() + (globalSettings.cacheLifetime ?? 8e3)
});
}
});
events.emit(SET_CACHE, keySelector, data);
};
function createMutatorStore(mutator) {
function createMutatorStore(mutator, opts) {
const { throttleCalls, onError } = opts ?? {
throttleCalls: true,
onError: globalSettings?.onError
};
const mutate = async (data) => {
var _a;
if (throttleCalls && store.value?.loading)
return;
const newMutator = rewrittenSettings.fetcher ?? mutator;
const keysToInvalidate = [];
const keysToInvalidate = [], keysToRevalidate = [];
const safeKeySet = (k, v) => {
if (store.lc) {
store.setKey(k, v);
}
};
try {

@@ -223,20 +292,25 @@ store.set({

},
getCacheUpdater: (key, shouldInvalidate = true) => [
revalidate: (key) => {
keysToRevalidate.push(key);
},
getCacheUpdater: (key, shouldRevalidate = true) => [
(newVal) => {
mutateCache(key, newVal);
if (shouldInvalidate) {
keysToInvalidate.push(key);
if (shouldRevalidate) {
keysToRevalidate.push(key);
}
},
cache.get(key)
cache.get(key)?.data
]
});
store.setKey("data", result);
safeKeySet("data", result);
return result;
} catch (error) {
(_a = globalSettings == null ? void 0 : globalSettings.onError) == null ? void 0 : _a.call(globalSettings, error);
onError?.(error);
safeKeySet("error", error);
store.setKey("error", error);
} finally {
store.setKey("loading", false);
safeKeySet("loading", false);
keysToInvalidate.forEach(invalidateKeys);
keysToRevalidate.forEach(revalidateKeys);
}

@@ -248,2 +322,6 @@ };

});
onStop(
store,
() => store.set({ mutate, ...notLoading })
);
store.mutate = mutate;

@@ -263,3 +341,3 @@ return store;

createMutatorStore,
{ __unsafeOverruleSettings, invalidateKeys, mutateCache }
{ __unsafeOverruleSettings, invalidateKeys, revalidateKeys, mutateCache }
];

@@ -274,11 +352,12 @@ };

}];
let keyStore = atom(null), keyParts = [];
const keyParts = [];
const $key = atom(null);
const keysAsStoresToIndexes = /* @__PURE__ */ new Map();
const setKeyStoreValue = () => {
if (keyParts.some((v) => v === null || v === void 0 || v === false)) {
keyStore.set(null);
$key.set(null);
} else {
keyStore.set([keyParts.join(""), keyParts]);
$key.set([keyParts.join(""), keyParts]);
}
};
const unsubs = [];
for (let i = 0; i < keys.length; i++) {

@@ -289,17 +368,23 @@ const keyOrStore = keys[i];

} else {
unsubs.push(
keyOrStore.subscribe((newValue) => {
keyParts[i] = isFetcherStore(keyOrStore) ? keyOrStore.value && "data" in keyOrStore.value ? keyOrStore.key : null : newValue;
setKeyStoreValue();
})
);
keyParts.push(null);
keysAsStoresToIndexes.set(keyOrStore, i);
}
}
const storesAsArray = [...keysAsStoresToIndexes.keys()];
const $storeKeys = batched(storesAsArray, (...storeValues) => {
for (let i = 0; i < storeValues.length; i++) {
const store = storesAsArray[i], partIndex = keysAsStoresToIndexes.get(store);
keyParts[partIndex] = store._ === fetcherSymbol ? store.value && "data" in store.value ? store.key : null : storeValues[i];
}
setKeyStoreValue();
});
setKeyStoreValue();
return [keyStore, () => unsubs.forEach((fn) => fn())];
return [$key, $storeKeys.subscribe(noop)];
};
function isFetcherStore(v) {
return v._ === fetcherSymbol;
function defaultOnErrorRetry({ retryCount }) {
return ~~((Math.random() + 0.5) * (1 << (retryCount < 8 ? retryCount : 8))) * 2e3;
}
const FOCUS = 1, RECONNECT = 2, INVALIDATE_KEYS = 3, SET_CACHE = 4;
function noop() {
}
const FOCUS = 1, RECONNECT = 2, INVALIDATE_KEYS = 3, REVALIDATE_KEYS = 4, SET_CACHE = 5;
const subscribe = (name, fn) => {

@@ -319,7 +404,5 @@ const isServer = typeof window === "undefined";

};
const getNow = () => (/* @__PURE__ */ new Date()).getTime();
const fetcherSymbol = Symbol();
const loading = { loading: true }, notLoading = { loading: false };
export {
nanoquery
};
export { nanoquery };
{
"name": "@nanostores/query",
"version": "0.2.10",
"version": "0.3.0",
"description": "Tiny remote data fetching library for Nano Stores",
"scripts": {
"pub": "pnpm build && npm publish && git push && git push --tags",
"prepublish": "pnpm build",
"pub": "pnpm test && pnpm build && npm publish && git push && git push --tags",
"build": "vite build",
"test:types": "tsc --noEmit && vitest typecheck --run",
"test:unit": "vitest run",
"test:unit": "vitest run --typecheck",
"test:size": "size-limit --silent",

@@ -44,24 +42,25 @@ "test": "pnpm run /^test:/",

"dependencies": {
"nanoevents": "^8.0.0"
"nanoevents": "^9.0.0"
},
"devDependencies": {
"@evilmartians/lefthook": "^1.4.8",
"@nanostores/react": "^0.7.1",
"@rollup/plugin-strip": "^3.0.2",
"@size-limit/preset-small-lib": "^8.2.6",
"@testing-library/react": "^14.0.0",
"@types/react": "^18.2.20",
"@types/react-dom": "^18.2.7",
"happy-dom": "^10.9.0",
"nanostores": "^0.9.3",
"@evilmartians/lefthook": "^1.6.9",
"@nanostores/react": "^0.7.2",
"@rollup/plugin-strip": "^3.0.4",
"@size-limit/preset-small-lib": "^11.1.2",
"@testing-library/react": "^14.3.0",
"@types/node": "^20.12.6",
"@types/react": "^18.2.75",
"@types/react-dom": "^18.2.24",
"happy-dom": "^14.7.1",
"nanostores": "^0.10.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"size-limit": "^8.2.6",
"typescript": "^5.1.6",
"vite": "^4.4.9",
"vite-plugin-dts": "^3.5.1",
"vitest": "^0.34.1"
"size-limit": "^11.1.2",
"typescript": "^5.4.4",
"vite": "^5.2.8",
"vite-plugin-dts": "^3.8.1",
"vitest": "^1.4.0"
},
"peerDependencies": {
"nanostores": ">0.7"
"nanostores": ">0.10"
},

@@ -80,3 +79,3 @@ "engines": {

},
"limit": "1626 B"
"limit": "1811 B"
}

@@ -83,0 +82,0 @@ ],

@@ -8,17 +8,8 @@ # Nano Stores Query

- **Small**. 1.62 Kb (minified and gzipped).
- **Familiar DX**. If you've used [`swr`](https://swr.vercel.app/) or
[`react-query`](https://react-query-v3.tanstack.com/), you'll get the same treatment,
but for 10-20% of the size.
- **Built-in cache**. `stale-while-revalidate` caching from
[HTTP RFC 5861](https://tools.ietf.org/html/rfc5861). User rarely sees unnecessary
loaders or stale data.
- **Revalidate cache**. Automaticallty revalidate on interval, refocus, network
recovery. Or just revalidate it manually.
- **Nano Stores first**. Finally, fetching logic *outside* of components. Plays nicely
with [store events](https://github.com/nanostores/nanostores#store-events),
[computed stores](https://github.com/nanostores/nanostores#computed-stores),
[router](https://github.com/nanostores/router), and the rest.
- **Transport agnostic**. Use GraphQL, REST codegen, plain fetch or anything,
that returns Promises.
- **Small**. 1.8 Kb (minified and gzipped).
- **Familiar DX**. If you've used [`swr`](https://swr.vercel.app/) or [`react-query`](https://react-query-v3.tanstack.com/), you'll get the same treatment, but for 10-20% of the size.
- **Built-in cache**. `stale-while-revalidate` caching from [HTTP RFC 5861](https://tools.ietf.org/html/rfc5861). User rarely sees unnecessary loaders or stale data.
- **Revalidate cache**. Automaticallty revalidate on interval, refocus, network recovery. Or just revalidate it manually.
- **Nano Stores first**. Finally, fetching logic *outside* of components. Plays nicely with [store events](https://github.com/nanostores/nanostores#store-events), [computed stores](https://github.com/nanostores/nanostores#computed-stores), [router](https://github.com/nanostores/router), and the rest.
- **Transport agnostic**. Use GraphQL, REST codegen, plain fetch or anything, that returns Promises (Web Workers, SubtleCrypto, calls to WASM, etc.).

@@ -38,10 +29,7 @@ <a href="https://evilmartians.com/?utm_source=nanostores-query">

See [Nano Stores docs](https://github.com/nanostores/nanostores#guide)
about using the store and subscribing to store’s changes in UI frameworks.
See [Nano Stores docs](https://github.com/nanostores/nanostores#guide) about using the store and subscribing to store’s changes in UI frameworks.
### Context
First, we define the context. It allows us to share the default fetcher
implementation between all fetcher stores, refetching settings, and allows for
simple mocking in tests and stories.
First, we define the context. It allows us to share the default fetcher implementation and general settings between all fetcher stores, and allows for simple mocking in tests and stories.

@@ -73,6 +61,7 @@ ```ts

const { data, loading } = useStore($currentPost);
if (data) return <div>{data.content}</div>;
if (loading) return <>Loading...</>;
if (!data) return <>Error!</>;
return <div>{data.content}</div>;
return <>Error!</>;
};

@@ -108,13 +97,22 @@

// How much time should pass between running fetcher for the exact same key parts
// default = 4s
// default = 4000 (=4 seconds; provide all time in milliseconds)
dedupeTime?: number;
// Lifetime for the stale cache. It present stale cache will be shown to a user.
// Cannot be less than `dedupeTime`.
// default = Infinity
cacheLifetime?: number;
// If we should revalidate the data when the window focuses
// default = false
refetchOnFocus?: boolean;
revalidateOnFocus?: boolean;
// If we should revalidate the data when network connection restores
// default = false
refetchOnReconnect?: boolean;
// If we should run revalidation on an interval, in ms
revalidateOnReconnect?: boolean;
// If we should run revalidation on an interval
// default = 0, no interval
refetchInterval?: number;
revalidateInterval?: number;
// Error handling for specific fetcher store. Will get whatever fetcher function threw
onError?: (error: any) => void;
// A function that defines a timeout for automatic invalidation in case of an error
// default — set to exponential backoff strategy
onErrorRetry?: OnErrorRetry | null;
}

@@ -133,11 +131,11 @@ ```

- `data` is the data you pass to the `mutate` function;
- `invalidate` allows you to mark other keys as stale so they are refetched next time;
- `invalidate` and `revalidate`; more on them in section [How cache works](#how-cache-works)
- `getCacheUpdater` allows you to get current cache value by key and update it with
a new value. The key is also invalidated by default.
a new value. The key is also revalidated by default.
```ts
export const $addComment = createMutatorStore<Comment>(
async ({ data: comment, invalidate, getCacheUpdater }) => {
// You can either invalidate the author…
invalidate(`/api/users/${comment.authorId}`);
async ({ data: comment, revalidate, getCacheUpdater }) => {
// You can either revalidate the author…
revalidate(`/api/users/${comment.authorId}`);

@@ -175,2 +173,14 @@ // …or you can optimistically update current cache.

`createMutatorStore` accepts an optional second argument with settings:
```ts
type MutationOptions = {
// Error handling for specific fetcher store. Will get whatever mutation function threw
onError?: (error: any) => void;
// Throttles all subsequent calls to `mutate` function until the first call finishes.
// default: true
throttleCalls?: boolean;
}
```
You can also access the mutator function via `$addComment.mutate`—the function is the same.

@@ -188,12 +198,7 @@

export const [,, { invalidateKeys, mutateCache }] = nanoquery();
export const [,, { invalidateKeys, revalidateKeys, mutateCache }] = nanoquery();
```
`invalidateKeys` does 2 things:
Both `invalidateKeys` and `revalidateKeys` accept one argument—the keys—in 3 different forms, that we call _key selector_. More on them in section [How cache works](#how-cache-works)
1. nukes all cache for the specified keys;
2. asks all the fetcher stores that used those keys to refresh data immediately, if they have active subscribers.
It accepts one argument—the keys—in 3 different forms, that we call _key selector_.
```ts

@@ -224,2 +229,33 @@ // Single key

### How cache works
All of this is based on [`stale-while-revalidate`](https://tools.ietf.org/html/rfc5861) methodology. The goal is simple:
1. user visits `page 1` that fetches `/api/data/1`;
2. user visits `page 2` that fetches `/api/data/2`;
3. almost immediately user goes back to `page 1`. Instead of showing a spinner and loading data once again, we fetch it from cache.
So, using this example, let's try to explain different cache-related settings the library has:
- `dedupeTime` is the time that user needs to spend on `page 2` before going back for the library to trigger fetch function once again.
- `cacheLifetime` is the maximum possible time between first visit and second visit to `page 1` after which we will stop serving stale cache to user (so they will immediately see a spinner).
- `revalidate` forces the `dedupeTime` for this key to be 0, meaning, the very next time anything can trigger fetch (e.g., `refetchOnInterval`), it will call fetch function. If you were on the page during revalidation, you'd see cached value during loading.
- `invalidate` kills this cache value entirely—it's as if you never were on this page. If you were on the page during invalidation, you'd see a spinner immediately.
So, the best UI, we think, comes from this snippet:
```tsx
// components/Post.tsx
const Post = () => {
const { data, loading } = useStore($currentPost);
if (data) return <div>{data.content}</div>;
if (loading) return <>Loading...</>;
return <>Error!</>;
};
```
This way you actually embrace the stale-while-revalidate concept and only show spinners when there's no cache, but other than that you always fall back to cached state.
### Local state and Pagination

@@ -255,3 +291,3 @@

1. `fetcherStore.invalidate`. It's a function that invalidates current key for the fetcher. Doesn't accept any arguments.
1. `fetcherStore.invalidate` and `fetcherStore.revalidate`
2. `fetcherStore.mutate`. It's a function that mutates current key for the fetcher. Accepts the new value.

@@ -264,3 +300,3 @@ 3. `fetcherStore.key`. Well, it holds current key in serialized form (as a string).

Let's say, you have a dependency for your fetcher, but you don't wish for it to be in your fetcher keys. For example, this could be your `refreshToken`—that would be a hassle to put it _everywhere_, but you need it, because once you change your user, you don't want to have stale cache from the previous user.
Let's say, you have a dependency for your fetcher, but you don't want it to be in your fetcher keys. For example, this could be your `userId`—that would be a hassle to put it _everywhere_, but you need it, because once you change your user, you don't want to have stale cache from the previous user.

@@ -273,3 +309,3 @@ The idea here is to wipe the cache manually. For something as big as a new refresh token you can go and do a simple "wipe everything you find":

But if your store is somehow dependant on other store, but it shouldn't be reflected in the key, you should do the same, but more targetly:
If your store is somehow dependant on other store, but it shouldn't be reflected in the key, you should do the same, but more targetly:

@@ -279,1 +315,11 @@ ```ts

```
### Error handling
`nanoquery`, `createFetcherStore` and `createMutationStore` all accept an optional setting called `onError`. Global `onError` handler is called for all errors thrown from fetcher and mutation calls unless you set a local `onError` handler for a specific store (then it "overwrites" the global one).
`nanoquery` and `createFetcherStore` both accept and argument `onErrorRetry`. It also cascades down from context to each fetcher and can be rewritten by a fetcher. By default it implements an exponential backoff strategy with an element of randomness, but you can set your own according to `OnErrorRetry` signature. If you want to disable automatic revalidation for error responses, set this value to `null`.
This feature is particularly handy for stuff like showing flash notifications for all errors.
`onError` gets a single argument of whatever the fetch or mutate functions threw.

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc