Nice video! I wonder though if the solution could be made more simple by creating a shared store, instead of the shared cache (Map). The getData function would return the existing store, followed by a call to the backend to update the value in the store. After the initial call to the backend the store has a value in it and no separate cache is necessary.
Really like this idea. Now just put that code in a service worker, and your app can work offline too with existing cache. A nice extra would be to add some subtle animation so the user notices that the page has updated if the contents change compared to the old cache.
I do something similar using localstorage. It works very well! However I would only use your specific show-stale-while-refresh technique for content that doesn't regularly change. That is, I decided to still show spinners for data that is dynamic and changes often (e.g. stock prices), because you would be showing incorrect data, and you don't know for sure for how long. As a user, seeing data and then seeing it change instantly without any feedback is *worse* than seeing a spinner. That being said, I get your point about avoiding showing a spinner flash for a split second, and that's why I opted to display a "placeholder block" instead (which is becoming more and more common nowadays), that way you get best of both worlds. either way, great video!
nice video, love it! but i have a question, so if any one want to help me out... thanks in this case, if you want to fetch new data, you would call getData function, right? it won't lead us instantiate new variables over and over again? because, you're always returning a new writable variable, and by doing that, you can lost the data reference in some component, no? otherwise, you would be repeating your fetch code and then call store update/set
Very nice but it seems like this will only work offline if you load the data first before setting airplane mode. Woudn't you need something similar to locastorage to make it work complelly offline? Another point, if you call the fetch inside the Svelte onMount function, it won't on fire on the second request which works kind of like a cache thing.
Loading in the new content after a period of time is definitely useful, but not if it causes a content or layout shift. For example, imagine I am looking at a page with news posts. I go to click on a post, but the posts shift because new content has just been loaded. Now I've clicked the wrong post. That would be very annoying.
Because it needs to update the data, if no load is called after returning a cached value you'll get stuck with the same data. What he is doing is basically "cache first"
@@jsachs Exactly! I tried to explain it as good as I can in the slides :) You still fetch the data on every call except you also cache data for faster responses and less spinners ;)
because this pattern uses svelte stores i cannot get it to work outside of the top level of a component or i get the error that svelte stores can only be used in the top level of a component.
outside of top level you need to manually attach to the subscription and destroy it when done...got it all working, now working on the SSR fetch implementation, and used cached when offline.
I use a similar technique with user-configurable translations. I have a store of the type Writable string> which defaults to returning an empty string. Then I am loading in the translations with a subsequent fetch, and provide a new implementation of the function. I can then just use the store like this: `{ $t("translation-key", "dynamic-value-1") }` And the ui will automatically receive the translations once they're fetched. And similarly, I can transparently cache the values. Or fire a beacon to say that a translation key has been used, etc. Or first load the defaults, then the user-provided values.
Hmm, maybe if you combine it with a loading spinner? User can view old data (and KNOWS it's old data) while the new data is coming in. It's an interesting technique, but I think it'd be quite jarring for a user to just instantly see the data update without any transition.
The code presented here has a subtle (and in most cases, possibly insignificant) memory leak: That initial newly-created promise passed to the `writable()` function never resolves and will never be garbage-collected. I wouldn’t have written the code this way, but good video nonetheless!
Thanks for the talk, but honestly, this is one of the worst User experience you can get. The User sees !wrong! old data and doesn't even know that there is something happening in the background. Maybe the user makes some bad decisions based on the wrong data. I can already see the support tickets comming in where the User complains about this. I think this should never be used somewhere. Don't get me wrong. The technique in this Video is cool and the talk was good, but the example is a bad use case for this technique.
Well It was quite hard for me to find an example which is easy to explain and show in 15 minutes yet still shows the difference between the "old" and the "new" way! I hope you still could enjoy my talk and get the technique :)
@@cstrnt The talk and the technique was good. I mean i don't have the balls to make a talk like this. So you can be proud of this. I think in certain use cases this technique can work. But in most use cases i think a spinner is the right thing to do.
@@cstrnt this thing can be useful for the use cases where paint something is important than painting stale data. Like product catalog , or weather updated, railway/flights updates
Nice! It's subtle things like this that really make the difference, thanks for sharing 👍✨
yeah, this is exactly what I am expecting: 'fetching external api in store', thank you for creating this tutorial, good job !!!
Nice video! I wonder though if the solution could be made more simple by creating a shared store, instead of the shared cache (Map). The getData function would return the existing store, followed by a call to the backend to update the value in the store. After the initial call to the backend the store has a value in it and no separate cache is necessary.
Wow, this is really interesting approach. Thanks a lot for the presentation and very well explained short demo.
Really like this idea. Now just put that code in a service worker, and your app can work offline too with existing cache. A nice extra would be to add some subtle animation so the user notices that the page has updated if the contents change compared to the old cache.
I do something similar using localstorage. It works very well! However I would only use your specific show-stale-while-refresh technique for content that doesn't regularly change. That is, I decided to still show spinners for data that is dynamic and changes often (e.g. stock prices), because you would be showing incorrect data, and you don't know for sure for how long. As a user, seeing data and then seeing it change instantly without any feedback is *worse* than seeing a spinner. That being said, I get your point about avoiding showing a spinner flash for a split second, and that's why I opted to display a "placeholder block" instead (which is becoming more and more common nowadays), that way you get best of both worlds.
either way, great video!
Great video! You have a talent for explaining things!
Awesome and simple explication !
smart dude
nice video, love it!
but i have a question, so if any one want to help me out... thanks
in this case, if you want to fetch new data, you would call getData function, right?
it won't lead us instantiate new variables over and over again? because, you're always returning a new writable variable, and by doing that, you can lost the data reference in some component, no?
otherwise, you would be repeating your fetch code and then call store update/set
Thank you for sharing this video
Very nice but it seems like this will only work offline if you load the data first before setting airplane mode.
Woudn't you need something similar to locastorage to make it work complelly offline?
Another point, if you call the fetch inside the Svelte onMount function, it won't on fire on the second request which works kind of like a cache thing.
Loading in the new content after a period of time is definitely useful, but not if it causes a content or layout shift. For example, imagine I am looking at a page with news posts. I go to click on a post, but the posts shift because new content has just been loaded. Now I've clicked the wrong post. That would be very annoying.
why you let the load function run, if the url already in the cache
Because it needs to update the data, if no load is called after returning a cached value you'll get stuck with the same data.
What he is doing is basically "cache first"
@@jsachs Exactly! I tried to explain it as good as I can in the slides :) You still fetch the data on every call except you also cache data for faster responses and less spinners ;)
because this pattern uses svelte stores i cannot get it to work outside of the top level of a component or i get the error that svelte stores can only be used in the top level of a component.
outside of top level you need to manually attach to the subscription and destroy it when done...got it all working, now working on the SSR fetch implementation, and used cached when offline.
could this be used for a full page of html?
Sehr gut, danke
Inspiring stuff!
I use a similar technique with user-configurable translations.
I have a store of the type Writable string> which defaults to returning an empty string.
Then I am loading in the translations with a subsequent fetch, and provide a new implementation of the function.
I can then just use the store like this:
`{ $t("translation-key", "dynamic-value-1") }`
And the ui will automatically receive the translations once they're fetched.
And similarly, I can transparently cache the values.
Or fire a beacon to say that a translation key has been used, etc.
Or first load the defaults, then the user-provided values.
Hmm, maybe if you combine it with a loading spinner? User can view old data (and KNOWS it's old data) while the new data is coming in. It's an interesting technique, but I think it'd be quite jarring for a user to just instantly see the data update without any transition.
The code presented here has a subtle (and in most cases, possibly insignificant) memory leak: That initial newly-created promise passed to the `writable()` function never resolves and will never be garbage-collected. I wouldn’t have written the code this way, but good video nonetheless!
Thanks for the talk, but honestly, this is one of the worst User experience you can get. The User sees !wrong! old data and doesn't even know that there is something happening in the background. Maybe the user makes some bad decisions based on the wrong data.
I can already see the support tickets comming in where the User complains about this. I think this should never be used somewhere.
Don't get me wrong. The technique in this Video is cool and the talk was good, but the example is a bad use case for this technique.
All depends on the use case i think. Im gonna use this to cache assets prices showing and updating every minute
Well It was quite hard for me to find an example which is easy to explain and show in 15 minutes yet still shows the difference between the "old" and the "new" way! I hope you still could enjoy my talk and get the technique :)
It really depends on how important the data in my opinion
@@cstrnt The talk and the technique was good. I mean i don't have the balls to make a talk like this. So you can be proud of this. I think in certain use cases this technique can work. But in most use cases i think a spinner is the right thing to do.
@@cstrnt this thing can be useful for the use cases where paint something is important than painting stale data. Like product catalog , or weather updated, railway/flights updates
KEKW
In
Why would you want so much logic in your global state just to apply it to one component, seems like over engineering to me.