Comments (19)
Thanks @aequasi !
We tested Local Storage and IndexedDb when started implementing this on the ZEIT dashboard. And we learned many things from it:
- Local Storage is IO heavy (it stores data on the disk), and cache read/write happens very frequently when the website starts to load. It will slow down the rendering and other resources.
- IndexedDb is async, which means the SWR cannot return the data from cache synchronously. So when navigating (back and forward) through the website with cache, React will always render without data first.
- and some other UX feedback about client side storage (e.g.: loading skeleton vs out of dated data from local cache when opening the website)
But it might be a good idea to support cache fallback (e.g.: Memory -> IndexedDb), expiration and other features. Anyway, supporting multiple caching mechanisms is definitely an important option. π
from swr.
We also extensively experimented with SSR for our dashboard, and the calculus is that it's just not worth it.
With Suspense + fast hydration + JS budgets + fast APIs, we'll never render a skeleton in most cases anyways. We'll suspend until we have data, then render it all at once in the ideal case.
The benefit? You get rid of all the server complexity, you only have a single error tracking and observability surface (the client side), and your TTFB is always consistently fast globally thanks to ZEIT CDN :chefkiss:
from swr.
In case it's helpful, here's useSWR with localforage as a persistent cache fallback that I use: https://gist.github.com/derindutz/179990f266e25306601dd53b8fbd8c6a. You can switch out localforage for whichever caching mechanism you want. I'm using it in production so if there are any suggestions you have for improvement I'm all ears π
from swr.
As an anecdote, we did an extensive experiment with IndexedDB and it was awful from a product perspective. Disks are slow and customers tend to want strong reads from the kind of apps and dashboards that you use SWR for.
There might be some use-cases for consumer apps where you're ok to restore to old data (like an Instagram feed) and then clearly designate as offline, where IndexedDB might make sense.
But even then I'd carefully consider the "network-first" strategy with a timer and things like that, kinda like workbox is approaching SW.
from swr.
Iβd love to see a layered cache architecture. for speed and such, i think the memory cache should always be the top level. and then the actual network requests are the lowest level
the ability to then add intermediate cache levels would be very powerful (for my usecases at least)
in other words, the first cache that should be checked shall remain the in memory map, which is sync, will ensure the above list works as expected. but then adding something like indexdb or websql as a layer before making a network request would be great
from swr.
Hello to all,
I wanted to offer you the possibility to use
wora/cache-persist (documentation). This library allows you to use all storage in synchronous mode through the use of a memory cache. All communication processes towards storage are queued and performed asynchronously. (natively can handle inmemorycache, localStorage, sessionStorage, indexedDB, AsyncStorage, customStorage )
I created this library to manage the persistence of Relay & Apollo and I also used it to manage offline mutations.
Let me know if you are interested in its integration.
from swr.
The cache Layer is already separate, you can import it with import { cache } from 'swr'
What I do to keep the SWR cache in sync with an off-line available storage (in web with localStorage, in RN you can use AsyncStorage I think), is to subscribe to cache changes and update the storage when something changes there and before rendering the app I read from that cache and fill the SWR cache with the data.
I built this library https://github.com/sergiodxa/swr-sync-storage to sync with WebStorage APIs, you can probably use something similar to sync with RN AsyncStorage or IndexedDB or another option.
This is a great way to work actually because some of those storage options are async by nature but you want to be able to read from cache in a sync way instead, this is why SWR cache is completely sync to get data, this allow SWR to read immediately from cache when data is already cache and revalidate in an async way, if SWR had to read from an async cache it should have to always send data
as undefined even if that data was already cached.
What you want to add here is a second cache layer, used for more persistent cache (in your case offline), so your AsyncStorage could be this second layer, where you will update data from the first layer and you will use it to fill the first layer before starting your app.
from swr.
Hi @quietshu,
I wrote to learn about the vision of the project. I agree with the need to make a library lighter and highly configurable (the goal of every well-made library :)) ... A curiosity, for you redux is light or not?
I agree with points 1 and 2, while with regard to point 3, I would suggest that you think about a concept of refresh & fetch policy, as found in Relay & Apollo.
Specifically, there are 4 types:
- store-or-network: Reuse data cached in the store; if the whole query is cached, skip the network request
- store-and-network: Reuse data cached in the store; always send a network request.
- network-only: Don't reuse data cached in the store; always send a network request.
- store-only: Reuse data cached in the store; never send a network request.
As for the integration of my library it is sufficient (points 1 & 2 are managed by the library):
- provide for the possibility of configuring a custom cache
- point 3
I think the topic of caching in the web & in react-native is a common problem in all open source projects and that's why I created this library. So any feedback from you will be very important to me.
Thanks
from swr.
Regarding IndexedDb, does it have to return synchronously?
It's okay to have async cache, and we can also read from memory cache because it's always a stream πthat's why a layered cache architecture (as @netspencer mentioned too π) can be very helpful.
from swr.
Hi guys,
to be able to do a PR (even in draft) I need some information from you.
Premise:
In order to manage all the storage it is necessary to provide a concept of asynchronous restore / hydration (indexeddb, asyncstorage etc ..). In this issue I described how this is managed in react-relay-offline
In short, it would be necessary to provide two phases:
-
the first one in which the application is offline (in react-native the detection of the network is asynchronous) & with the empty cache (or with the data received from the SSR server)
- the restore function is performed (both to detect the network and to retrieve data from storage)
- fetch is not performed
- the first renderer is executed in a loading state or with the data retrieved from the server
-
the second phase is activated by the restore callback
- the state reconciliation is performed between the state present in the store and the data recovered from the server (if present)
- the network state is detected
- the hook is refreshed
Both wora/cache-persist and the library mentioned in this issue #69 natively manage this behavior.
It would be useful to have your indication / collaboration on how to create and configure the cache externally and how to better manage the two phases in useSWR.
from swr.
Closing this issue as I believe most of our goals are covered by the 1.0 release: https://swr.vercel.app/blog/swr-v1.
from swr.
Fair points @quietshu !
Regarding IndexedDb, does it have to return synchronously? Presumably, if you are wanting to use IndexedDb, you would have to accept the fact that its asynchronous, and you would have to have the 'non-data' view.
from swr.
ideally weβd be able to specify a faster cache (like lru or redis) for SSR though, so the user never sees a flash of no data
from swr.
Hi @morrys,
Thank you for your input! I think what you have proposed consists of these features:
- support of async cache
- cache provider API which supports events too
- API to pause/resume fetching
Instead of making all the cache options (IndexedDB, ...) and network detectors built-in, using them
as APIs will be much more flexible. So people can build their own cache or use a plugin if they really need to customize the cache machinesm behind, and for 90% users who don't need that, SWR will still be lightweight and highly customizable.
I think the third feature of above will be the easist to implement (just a boolean in the options / config provider). But the first 2 things require a lot of changes still.
from swr.
Thanks @morrys!
First of all Redux is very lightweight. It's just a 3kB lib (like SWR) but most importantly it's just a simple and powerful concept (Reducer). But there're a lot of plugins and libs around that basic concept.
Same for this lib. "SWR" is the concept of stale-while-revalidate, in which:
- stale is the cached data
- revalidate is the way to get the fresh data
That's why I think we need make each part customizable:
- data: cache API (or SSR, local state)
- request: fetcher function, network options, subscription, etc.
instead of extending the SWR concept with more features. And I believe the scenarios you provided can be implemented with those 2 APIs too.
And for sure we can make it easier for plugins to extend this lib.
from swr.
Exactly @quietshu :) the important thing is to consider the possibility of extending the concept. I think some things should have been handled natively in redux (but that's another story).
At this point I would say that the main theme is to make the cache customizable. Is it a work in progress?
from swr.
I'm working on a react native app that needs to be synced with server all the time but frequency of data changes for some of them are low so we can show the cached version while revalidating (even if the user is offline).
SWR are storing data in memory and data will be lost in some situation like closing app. it's not offline-capable as well.
I read the whole thread and there are some valid points. Unfortunately I had to switch to my SWR alternative solution which is initializing from storage (and put in memory), write/read from memory and sync with storage after revalidating (write), and then same API as SWR.
I think SWR can fix this by separating the cache layer. because the only thing I had to change was changing the cache location. by providing some APIs, we can have the built-in memory cache and at the same time let others to implement their own cache layer like IndexedDB, ... as a separate package.
from swr.
Interesting @sergiodxa, Thanks for sharing. I checked your code out, I only have one concern which is it seems it's not possible to keep them in sync efficiently right now because swr's notify
doesn't passing the affected item, it just notify the listeners something has changed. what I saw in your code as well is you're reading the whole caches and putting them in storage again after each change.
As other guys mentioned IO is a heavy task and I'm not sure how the performance will be for applications with a large cache/data.
What I noticed here is there is no way to :
- recognize only the affected cache
- distinguish set/delete/clear in
subscribe
to perform the right action for it.
from swr.
There is a PR I opened it to allow subscribed to know the updated key #365, that will improve the way to do this kind of second cache layer support.
from swr.
Related Issues (20)
- Wrong key serialization HOT 5
- React Hook Order Error with Multiple useSWR Hooks in Next.js with Suspense HOT 2
- useSWRInfinite with types
- Revalidate useSWR by considering only the first argument when using multiple arguments. HOT 1
- focusThrottleInterval is not honoured for the first refresh on focus after mount HOT 3
- auto retry has timer issues with mutate
- `useSWR` in UI KIT library not using cache from consumer app HOT 7
- isLoading always typed as false when using fallbackData
- useSWR not making the fetch request
- useSWR deduped by mistake after cache clear HOT 1
- useSWRInfinite continually fetches the first page (no caching observed) HOT 3
- Type mismatch on SWR Configuration
- Got "None of the selected packages has a "watch" script" when pnpm watch run HOT 2
- useSWR seems to trigger suspense boundaries even if not using suspense mode HOT 5
- Type error when overriding global fetcher function with null HOT 3
- SWR mutate does not trigger even if I have a hook mounted with the same key HOT 1
- fallback doesn't work with `useSWRInfinite` and next app router HOT 1
- Type error when fallback option with undefined.
- Repeat render
- useSWRMutation - Type error: Expected 2-3 arguments, but got 1. HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. πππ
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google β€οΈ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from swr.