Dealing with async assets - A promise caching strategy for React Suspense
Last update: May 10, 2022
Dealing with async assets
Each asset you create comes with its own cache. When you request something from it, the arguments that you pass will act as cache-keys. If you request later on using the same keys, it won't have to re-fetch but serves the result that it already knows.
{
// Any async task can run in here, fetch requests, parsing, workers, promises, ...
const res = await fetch(`https://hacker-news.firebaseio.com/${version}/item/${id}.json`)
return await res.json()
})
function Post({ id }) {
// Then read from it ...
const { by, title } = asset.read(id, "v0") // As many cache keys as you need
// By the time we're here the async data has resolved
return
{title} by {by}
}
function App() {
loading...
}>
}">
importReact,{Suspense}from"react"import{createAsset}from"use-asset"// Create a cached sourceconstasset=createAsset(async(id,version)=>{// Any async task can run in here, fetch requests, parsing, workers, promises, ...constres=awaitfetch(`https://hacker-news.firebaseio.com/${version}/item/${id}.json`)returnawaitres.json()})functionPost({ id }){// Then read from it ...const{ by, title }=asset.read(id,"v0")// As many cache keys as you need// By the time we're here the async data has resolvedreturn<div>{title} by {by}</div>}functionApp(){<Suspensefallback={<div>loading...</div>}><Postid={10000}/></Suspense>}
Preloading assets
// You can preload assets, these will be executed and cached immediatelyasset.preload("/image.png")
Cache busting strategies
// This asset will be removed from the cache in 15 secondsconstasset=createAsset(promiseFn,15000)// Clear all cached entriesasset.clear()// Clear a specific entryasset.clear("/image.png")
Peeking into entries outside of suspense
// This will either return the value (without suspense!) or undefinedasset.peek("/image.png")
Hooks and global cache
You can also use the useAsset hook, which is modelled after react-promise-suspense. This makes it possible to define assets on the spot instead of having to define them externally. They use a global cache, anything you request at any time is written into it.
{
// Any async task can run in here, fetch requests, parsing, workers, promises, ...
const res = await fetch(`https://hacker-news.firebaseio.com/${version}/item/${id}.json`)
return await res.json()
}, id, "v0") // As many cache keys as you need
// By the time we're here the async data has resolved
return
{title} by {by}
}
function App() {
loading...
}>
">
import{useAsset}from"use-asset"functionPost({ id }){const{ by, title }=useAsset(async(id,version)=>{// Any async task can run in here, fetch requests, parsing, workers, promises, ...constres=awaitfetch(`https://hacker-news.firebaseio.com/${version}/item/${id}.json`)returnawaitres.json()},id,"v0")// As many cache keys as you need// By the time we're here the async data has resolvedreturn<div>{title} by {by}</div>}functionApp(){<Suspensefallback={<div>loading...</div>}><Postid={1000}/>
Cache busting, preload and peeking
The hook has the same API as any asset:
// Bust cache in 15 secondsuseAsset.lifespan=15000useAsset(promiseFn,"/image.png")// Clear all cached entriesuseAsset.clear()// Clear a specific entryuseAsset.clear("/image.png")// Preload entriesuseAsset.preload(promiseFn,"/image.png")// This will either return the value (without suspense!) or undefineduseAsset.peek("/image.png")
a suggestion to change the api: args: any[] -> arg: any
custom areEqual with Object.is by default. deepEqual optional.
Hope you like it!
[x] impl
[x] readme
[ ] examples
Reviewed by dai-shi at 2020-10-22 11:43
2. Increase fetching threshold for infinite load on scroll example
I'm looking into using react-three-fiber and use-asset for one of my projects, and I've noticed that the infinite load example doesn't work on my display. Looking into the code, this condition for triggering fetching may never be true for larger screens:
It's a very minor thing, but I was wondering if it makes sense to increase this threshold and make it independent of the screen size, e.g. something like:
And thanks very much for all the awesome work on the poimandres familiy of libraries! I really appreciate how you use the most interesting features of React to make sure that all the different use cases stay performant and simple to use. :+1:
Reviewed by Doesntmeananything at 2020-11-07 11:48
3. Add support for asset typings
So that Typescript knows what type of variable is returned from e.g. asset.read() and you don't have to do something like this when consuming the asset:
const stuff = asset.read( name, ...args ) as StuffType // `as StuffType` should ideally not be necessary
Reviewed by rijk at 2020-11-04 13:34
4. Readme: titles barely readable with dark theme
I use the dark theme and the black titles are not that distinguishable from the background.
To enable dark theme go to Settings > Appearance > select Dark
Reviewed by marcofugaro at 2021-01-21 13:58
5. Return type definitions for createAsset API
This allows the user to use an asset (especially useful for one that is exposed by a package) without knowing the type beforehand.
In user code (no knowledge of type SubscriptionAsset):
I also added JSDocs for the throw behaviour:
Reviewed by rijk at 2020-11-05 10:35
6. Allow asset hydration
Some frameworks like Next performs data prefetching in the server-side, providing the initial data for the whole app. Libraries like swr and react-query takes advantage of this by using the prefetched data as the initial cache data and performs the fetch process/cache invalidation only on client-side.
Asset hydration helps because React SSR cannot handle nor does not support Suspense (CM, however, supports this) and since the asset is guaranteed to have an initial data, the component does not have to suspend during SSR and the potentially suspending component can safely present its success UI.
I think something like asset.hydrate(args, data) or useAsset(asset, args, initialData) is a good API. Thoughts?
React-spa-portfolio is a react portfolio build with React, SASS and includes futures like carousal (build with pure react no library), tabs, and others.
This is a React application for managing books you want to read. It tracks your progress and makes reading the books enlisted as a goal. The project is built using HTML, CSS, JavaScript, React, and Redux
This is the web frontend of the NLW heat 2021 project, carried out by rocketseat. It is free to use, and any questions or errors, please do not hesitate to consult me, or post an issue. In this code we used: React; Typescript/TSX; Vite; Socket.io; Scss/Sass; and other libs for react (axios, context...).