Replies: 2 comments
-
The fact that SWR is so tightly integrated with React is not allowing me to use it in a project either. For the future it would be great to decouple the React logic from the main SWR functionality. I've open this discussion too #2853 |
Beta Was this translation helpful? Give feedback.
-
Update. Have discussed this again after a year with a few friends. And there is the idea about conditional fetch:
useSWR(atom); // need fetch
useSWR(atom, /** fetcher args */ false); // no fetch
useSWR(atom, /** fetcher args */ null); // no fetch
useSWR(atom, /** fetcher args */ { pageIndex: 1, pageSize: 10 }); // need fetch, fetcher arg not falsy An atom will only initiate fetch when the first
As for middleware, we can extend an atom using a createResource().with(localStoragePersistent)
createResource().with(logger) And developers can made their own const createResourceWithLogger = () => createResource().with(logger); |
Beta Was this translation helpful? Give feedback.
-
Disclaimer: I am not a member of the SWR Team. This RFC is intended to share some of my naive ideas. The API design and potential usage I described in this RFC may or may not be approved and implemented by the SWR Team. And if the RFC is approved, the API name/interface will most likely change.
Motivations
Both SWR and Tanstack Query use user-provided keys for deduplication, caching, and cache revalidation.
Tanstack Query and SWR are used in React applications of all sizes. They have proven that key-based caching/deduplication works at scale. However, it still has some limitations:
You will almost always have to extract your logic into a custom hook
It is common to request the same data inside different components:
Now we have repeated the key and the fetcher. And we may accidentally pass different fetchers and options to them:
To remove the repetitive code and to avoid passing different fetchers/options, the best approach for us is to extracting a custom hook:
Even if we use a custom hook, we still can't prevent repeated key/fetcher
Some SWR APIs still require you to provide a key/fetcher. E.g. programmatically prefetch:
Although SWR also allows you to programmatically prefetch data without providing a key through the "bound method"...
...the use of the "bound method" is severely limited, as you can only use React Hooks within a React Component. In this case, we can't perform prefetch outside of React using the "bound method".
We can't avoid the key when accessing SWR outside of React
Since SWR is a React Hook, we can't access SWR outside of React. In order to contact SWR from outside of React (Let's say, providing pre-filled data from SSR), we still have to use the key as a bridge.
Let's use this Next.js example from SWR's documentation:
If your key is very complex, you will have to use the
unstable_serialize
. But if you don't want to deal with the key,unstable_serialize
and theSWRConfig
, you will have to implement your ownSWRConfig
. Your code will quickly turn into spaghetti:The internal implementation of SWR is bound with React
SWR maintains a global store outside of React, caches the data outside of React (in its external global store), and uses
useSyncExternalStore
to sync the cache back into React.However, SWR is still tightly bound to the React lifecycle:
useSWR
reads your fetcher and option within React's lifecycle. Most of SWR's features are built on top of React'suseLayoutEffect
, such as "laggy data", auto revalidation (the listeners for the focus event and the network change event are attached and detached withinuseLayoutEffect
, and the timer for revalidating on an interval is also added withinuseLayoutEffect
), and mutation.Inspiration
I have been playing around with the jotai (a React state management library with an atomic model) for a while. For those who haven't used jotai before, here is a basic example of jotai:
The state is stored inside atoms, outside React. Atoms rely on referential identities instead of keys. To access these states inside React, you pass the atom directly to the
useAtom
(oruseAtomValue
) hook. The hook then subscribes to the atom, syncs the state into React, and causes React to render only when the atom is updated.So this hits me, what happens if we can define our fetcher and SWR options using an atom-like API?
Basic Example
You can also populate prefetched data from the server directly into the resource, outside of React:
Detailed Design
Each resource is created with a fetcher and an option. Once created, the
resource
will have its own cache and lifecycle. Theresource
will handle calling the fetcher, storing the response, and managing the cache and revalidation, all by itself.The
useSWRFuture
hook is used to read and subscribe to a resource.Request deduplication is based on referential identities rather than keys. And because each resource manages its lifecycle, it eliminates race conditions since each resource will only send one request at a time within its lifecycle. And multiple
useSWRFuture()
calls will always result in one request since the fetcher is called by the resource itself, not by the hook.Since each resource will manage its lifecycle outside of React, auto revalidation and mutation won't require useEffect/useLayoutEffect anymore. The resource will send the initial request, receive the mutation, perform the revalidation all on its own, and notify the React to update (via
useSyncExternalStore
).Drawbacks
Breaking Changes
It works much differently than the current SWR. We will have to rewrite the entire documentation to teach people how to use it.
It would also take a lot of time to adopt the new API for large existing codebases. Most likely SWR will have to keep the current API and behavior for at least 3 major version bumps to be backward compatible.
Conditional Request
It is very easy to achieve conditional requests with the current
useSWR
:However, since resources manage their lifecycles outside of React, we will have to design an API that is not counterintuitive:
Dynamic URL
SWR uses keys not only for caching and deduplication, but the keys will also be passed to the fetcher as the argument. It enabled SWR to fetch dynamic URLs like this:
But if we define resources outside React, it could be difficult to pass dynamic URLs to resources (or at least counterintuitive):
Middleware
Because resources are atomic and defined outside of React, it is difficult to add and extend middleware to each resource.
Beta Was this translation helpful? Give feedback.
All reactions