[RFC] Subscription mode for SWR
See original GitHub issueDefine the Problem
currently useSWR
and useSWRInfinite
are more designed for endpoints or data sources that can directly access and patch proactively. The data flow is like below
useSWR <=== write and read ====> data source
But for some cases SWR doesn’t have enough solid ability to interact with data sources such as observable data (states returned from an observable stream), subscribable data (data been sent from a remote websocket endpoint).
then the fetcher
of SWR will lose meaning since they’re not really accessible initiavely from user end. we can surly do a subscribe
or observe
call to attach on the source. however we cannot know the state of a certain time.
besides fetcher
, the mutate
, trigger
and revalidate
APIs are also kind f disabled due to the passive mode. I want to find a new way to think of the communication path.
Solution
propose to bring a new hook API useSWRSubscription
to SWR to fit in the subscribable situation. unlike the basic useSWR hook, it won’t return the manipulation APIs of useSWR.
API
function useSWRSubscription(key, subscribeFn: (onData, onError) => Disposable): {data?, error?};
Usage
// like the useSWRInfinite, we put it into an extra file
// or in the future we could make it import useSWRSubscription from 'swr/subscription'
import {useSWRSubscription} from 'swr'
const {data, error} = useSWRSubscription('wss://my.app/api/chat-messages', subscribeWs)
function subscribeWs(key, {onData, onError}) {
const ws = new WebSocket(key)
ws.onmessage = (event) => {
onData(event.data)
}
ws.onerror = (error) => {
onError(event.data)
}
return () => ws.close()
}
then in this case, if the key changed, SWR is able to close and re-subscribe to the source for new key; the uncertain thing is the returned error might not always be valid since some obseravle won’t throw error when the connection get closed;
Other Possible Solutions
make revalidate and returned trigger with no effect. but mutate is still able to patch states to that key
Pros & Cons
Pros
- support realtime data
- adaptive to any observable data source, user can also do customization upon it
- simple returned values to let user to leverage, no mind load on the meaning of revalidate/mutate/trigger
Cons
- if there’s any new API can access the current data, like geo API is subscribable but getting current position of geo is also doable. might need to have some workaround for that…
- more APIs into SWR, might bring confusion to new users to choose API
Issue Analytics
- State:
- Created 3 years ago
- Reactions:7
- Comments:9 (7 by maintainers)
Just come to mention that my PR (#457) come from a different use-case than the idea of the RFC.
The
useSWRObservable
hook is to get data from an observable source without when you don’t have a non-observable source. This is useful when your source of data is only WebSockets or something real-time so you can’t use a fetcher, it makes total sense here.My proposal to add a
subscribe
in the config comes from a different idea where you have an API you can fetch with a fetcher but you want to start a subscription to an observable-source to receive updates without using long-polling.Imagine this flow, in your fetcher you do a
GET /api/resource/1
to get the resource with ID 1, then your subscribe to/ws//resource/1
in a WS server, this way you get an initial up-to-date data for your resource and thank to the subscription if something change in the server you don’t need to wait for SWR to revalidate it, the API will let you know, and the reason to pass mutate is that sometimes this WS servers can return the new data, sometimes they return a delta of what changed so you will need to call mutate to apply the update, and sometimes they return that something changed and you will need to trigger a revalidation against the HTTP API.So, I think we should support both,
useSWRObservable
and thesubscribe
option inuseSWR
.Thank you for writing this amazing RFC!
This doesn’t solve the data problem right? Like how do I notify SWR that I got the data (or error).
I think we can probably also return a
connecting
orloading
value, which indicates the initial connection state (before the first value arrives).I’m 👍 on doing it.