solid-swr
v5.0.1
Published
The beloved swr package ported over to solid
Downloads
108
Maintainers
Readme
Introduction
Quote from vercel's SWR for react:
The name “SWR” is derived from stale-while-revalidate, a HTTP cache invalidation strategy popularized by HTTP RFC 5861. SWR is a strategy to first return the data from cache (stale), then send the fetch request (revalidate), and finally come with the up-to-date data.
With SWR, components will get a stream of data updates constantly and automatically. And the UI will be always fast and reactive.
Features
- 💙 Built for solid
- ⚡ Blazingly fast with reconciled solid stores and zero* extra hook allocation
- ♻️ Reusable and lightweight data fetching
- 📦 Optional built-in cache and request deduplication
- 🔄 Local mutation (optimistic UI)
- 🔥 0 dependencies
- 😉 And much more!
For v4 docs readme
Install
pnpm i solid-swr
Quick start
import { useSwr, SwrProvider, Store } from "solid-swr"
import { LRU, createCache } from "solid-swr/cache"
function App() {
const { v, mutate, revalidate } = useSwr(() => "/api/user/2")
const onClick = () => {
mutate({ name: "user2" })
// if you need to revalidate
revalidate()
}
return (
<div onClick={onClick}>
{v().isLoading}
{v().data}
</div>
)
}
function Root(props) {
return (
<SwrProvider value={{ store: new Store(createCache(new LRU())) }}>
{props.children}
</SwrProvider>
)
}
Explanation
Hook returns 3 values which you can destructure:
v
: function that indexes into solid storemutate
: basicallysetStore
but scoped to the keyrevalidate
: call fetcher again (not guaranteed to be called due to deduplication), this function returns the result of the fetch call
Ideaology
Here I want to share some context about the ideaology of this library and swr in general
The "key"
The key is a string
which is used as an ID into some server side state
const key = "api/user/id"
useSwr(() => key)
The key is almost always used as a url to a backend resource
Solid store as a source of truth
Everything is stored in a solid store, i.e. isLoading, data, err, etc...
All hooks / utilities, talk to a single object through a Store
interface
This way, solid handles the syncing of changes to listeners, thus:
- we avoid implementing syncing cough cough
solid-swr@v4
- we avoid duplicating large amounts of js objects, again cough cough
solid-swr@v4
- And most importantly, this gives us
O(1)
time complexity to calluseSwr
Behavior can be customized through public core APIs
In v5, useSwr
is a core hook, meaning that it is simple and meant to be extended
In fact, useSwr
is just a simple function that uses other public apis:
createRevalidator
createMutator
An excerpt from useSwr
as of the time I'm writing this
const runWithKey = <T extends (k: string) => any>(fn: T): ReturnType<T> | undefined => {
const k = key();
if (!k) return;
return fn(k);
};
const revalidator = createRevalidator(ctx);
const mutator = createMutator(ctx);
// as you can see, revalidate is just a convenience method to call revalidtor
const revalidate = () => runWithKey(k => revalidator<D, E>(k));
// mutate is exactly the same
const mutate = (payload: Mutator<D>) => runWithKey(k => mutator<D, E>(k, payload));
If you at any time need a revalidator, or a mutator, just use createRevalidator
or createMutator
,
or create new abstractions with these 2, just like pretty much all hooks in this lib
Core
This is the most important part of the library which contains all core utilities for you to manage server side state with swr
Store
This is by far THE most important part of the library
The store is a solid.js store object with the key string
as the key
export type SolidStore = {
[key: string]: StoreItem | undefined;
};
Each item in the store contains these properties:
data
: a generic valueerr
: a generic valueisLoading
: a boolean
For more info I suggest you looking at the src/store.ts
everything is there
Cache
A separate user-provided cache is used to remove items from the store
Connect your cache with the store like so:
export type StoreCache = {
/** item has been inserted into store */
insert: (key: string, onTrim: OnTrimFn) => void;
/** item has been looked up */
lookup: (key: string, onTrim: OnTrimFn) => void;
};
const store = new Store({
lookup: (key, onTrim) => lru.get(key),
insert: (key, onTrim) => lru.set(key, true, onTrim)
})
solid-swr
provides this behavior ootb
import { LRU, createCache } from "solid-swr/cache"
new Store(createCache(new LRU()))
The onTrim
is how the store connects to the cache,
call onTrim(key)
to remove a key from the solid store
In the case above when lru
tries to set a key it will trim the cache,
thus removing (if needed) a key
Methods
Store can be mutated / read with its public methods
lookupOrDef
: gets the correct item or returns defaultupdate
: update store while reconciling dataupdateProduce
: update store with solidproduce
util
createRevalidator
import { createRevalidator } from "solid-swr"
Create a function that revalidates (calls fetcher) a key
This function also deduplicates requests, so when you call it, the actual fetcher call is not guaranteed
createMutator
import { createMutator } from "solid-swr"
Create a function that can change any key in the store
useSwr
import { useSwr } from "solid-swr"
Hook that uses createRevalidator
and createMutator
to create the swr behavior
that we all love
Returns:
mutate
:createMutator
scoped to a keyrevalidate
:createRevalidator
scoped to a keyv
: a function that indexes into a solid store
Options
src/core.ts
export type SwrOpts<D = unknown, E = unknown> = {
store: Store;
fetcher: (key: string, { signal }: FetcherOpts) => Promise<unknown>;
/** gets direct store references (don't mutate) */
onSuccess: (key: string, res: D) => void;
/** gets direct store references (don't mutate) */
onError: (key: string, err: E) => void;
/** gets direct references to response (don't mutate) */
onSuccessDeduped: (key: string, res: D) => void;
/** gets direct reference to response (don't mutate) */
onErrorDeduped: (key: string, err: E) => void;
};
Passing options
Options can be passed either to a useSwr
hook instance or
with SwrProvider
Reading options
Options can be read with useSwrContext
Extra
import * as extra from "solid-swr/extra"
All of the recipes shown here could have been created by using the #core utils
If you have come up with an awesome recipe that's not shown here,
I would love to add it to solid-swr
I encourage you to take a look at src/extra.tx
to get more context about inner
workings of these recipes
useSwrFull
This is similar to the default swr in solid-swr@v4
Basically it is core hook with extra options:
export type SwrFullOpts = {
keepPreviousData: boolean;
revalidateOnFocus: boolean;
revalidateOnOnline: boolean;
fallback: Fallback;
refreshInterval: number;
};
Setting these options is the same as in core but with useSwrFull*
utils
useMatchMutate
Uses createMutator to mutate multiple keys at once
useMatchRevalidate
Uses createRevalidator to revalidate multiple keys at once
This hook also skips revalidating items from the store which do not have any hooks attached to them,
this is known by looking at _mountedCount
number
useSwrInfinite
Used for infinite loading, returns an array of accessors into correct store index
import { useSwrInfinite } from "solid-swr/store"
const { data } = useSwrInfinite((index, prevData) => `https://example.com?page=${index}`)
// here we get first item, then we access the store with second ()
// then get the actual `data` that we need
const firstItemData = data()[0]().data