Dealing with async assets - A promise caching strategy for React Suspense

Last update: May 10, 2022

This library allows you to create cached assets, which can be promises, async functions or even dynamic imports. These assets then have the ability to suspend the component in which they are read. This makes it easier to orchestrate async tasks and gives you the ability to set up fallbacks and error-handling declaratively.

Build Size Build Status Version Downloads

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...
}> }">
import React, { Suspense } from "react"
import { createAsset } from "use-asset"

// Create a cached source
const asset = createAsset(async (id, version) => {
  // 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 <div>{title} by {by}</div>
}

function App() {
  <Suspense fallback={<div>loading...</div>}>
    <Post id={10000} />
  </Suspense>
}

Preloading assets

// You can preload assets, these will be executed and cached immediately
asset.preload("/image.png")

Cache busting strategies

// This asset will be removed from the cache in 15 seconds
const asset = createAsset(promiseFn, 15000)
// Clear all cached entries
asset.clear()
// Clear a specific entry
asset.clear("/image.png")

Peeking into entries outside of suspense

// This will either return the value (without suspense!) or undefined
asset.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"

function Post({ id }) {
  const { by, title } = useAsset(async (id, version) => {
    // 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 <div>{title} by {by}</div>
}

function App() {
  <Suspense fallback={<div>loading...</div>}>
    <Post id={1000} />

Cache busting, preload and peeking

The hook has the same API as any asset:

// Bust cache in 15 seconds
useAsset.lifespan = 15000
useAsset(promiseFn, "/image.png")
// Clear all cached entries
useAsset.clear()
// Clear a specific entry
useAsset.clear("/image.png")
// Preload entries
useAsset.preload(promiseFn, "/image.png")
// This will either return the value (without suspense!) or undefined
useAsset.peek("/image.png")

Recipes

Simple data fetching

Fetching posts from hacker-news: codesandbox

Infinite load on scroll

Fetching HN posts infinitely: codesandbox

Async dependencies

Component A waits for the result of component B: codesandbox

GitHub

https://github.com/pmndrs/use-asset
Comments
  • 1. [breaking change] new api with areEqual

    1. make types strict (no-any)
    2. a suggestion to change the api: args: any[] -> arg: any
    3. 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:

    e.target.scrollTop > content.current.scrollHeight - 1000
    

    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:

    e.target.scrollTop > content.current.scrollHeight / 2
    

    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

    image

    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.

    For example, in package:

    
    type SubscriptionAsset = {
      computation: Tracker.Computation
      subscription: Meteor.SubscriptionHandle
    }
    
    export const subscriptions = createAsset<SubscriptionAsset>( ( name, ...args ) => {
      // ...
    })
    

    In user code (no knowledge of type SubscriptionAsset):

    Screenshot 2020-11-05 at 11 31 14

    I also added JSDocs for the throw behaviour:

    Screenshot 2020-11-05 at 11 29 30
    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?

    Reviewed by LXSMNSYC at 2020-11-11 06:53
Related tags
A calculator app built with React.js using React Hooks and Routers. The app contains 3 pages with basic styling. Calculator operations, as well as React components, were tested with Jest and React Testing Library.
A calculator app built with React.js using React Hooks and Routers. The app contains 3 pages with basic styling. Calculator operations, as well as React components, were tested with Jest and React Testing Library.

Raect Calculator A calculator app built with React.js using React Hooks and Routers. App contains 3 pages with basic styling. Calculator operations, a

Sep 19, 2021
olx-clone using react js , firease as backend ,deployed in firebase , react-router,context,react hooks,olx web app project

OLX clone Start your own website like Olx in minutes with help of Sijeesh Miziha's Olx-Clone React App. Olx-Clone is the potential classified advertis

May 9, 2022
netflix clone using react js, css, axios, themoviedb, get api, netflix-clone react simplified project, axios-react
netflix clone using react js, css, axios, themoviedb, get api, netflix-clone react simplified project, axios-react

Netflix Clone React js This project is a simplified front end clone of Netflix. It was created with React.js and CSS,It uses the famous Javascript lib

Jan 21, 2022
React-spa-portfolio is a react portfolio build with React, SASS and includes futures like carousal

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.

Dec 7, 2021
React Reduction - Free Admin Template Built with React and Bootstrap4
React Reduction - Free Admin Template Built with React and Bootstrap4

React Reduction - Free Admin Template Built with React and Bootstrap4 Preview You can check out live preview. Quick Start Clone the repo git clone htt

May 11, 2022
A Netflix clone created using React. It is built using modern react tools/best practices, such as hooks, context API, and redux toolkit.
A Netflix clone created using React. It is built using modern react tools/best practices, such as hooks, context API, and redux toolkit.

Netflix-Clone A Netflix clone I created for the sake of practicing React and Redux. It features design patterns recommended by the documentation. Some

May 10, 2022
A simple project to display clean and concise Covid-19 Statistics by country. ⚛️ React.js ⚛️ React Query 💅 Tailwind CSS 🔎 Google Analytics
A simple project to display clean and concise Covid-19 Statistics by country. ⚛️ React.js ⚛️ React Query 💅 Tailwind CSS 🔎 Google Analytics

A simple project to display clean and concise Covid-19 Statistics by country. ⚛️ React.js ⚛️ React Query ?? Tailwind CSS ?? Google Analytics

Apr 6, 2022
React features to enhance using Rollbar.js in React Applications

React features to enhance using Rollbar.js in React Applications

May 12, 2022
A simple React zing mp3 apps built with React Typescript, Redux and TailwindCSS.
A simple React zing mp3 apps built with React Typescript, Redux and TailwindCSS.

React Typescript Zingmp3 A simple React zing mp3 apps built with React Typescript, Redux and TailwindCSS. Working application Checkout: https://60a609

Apr 23, 2022
A React & react-router-powered implementation of Hacker News using its Firebase API.
A React & react-router-powered implementation of Hacker News using its Firebase API.

react-hn A React & react-router-powered implementation of Hacker News using its Firebase API.

May 9, 2022
Engin Demiroğ, Java + React bootcamp HRMS project front-end codes (React JS).
Engin Demiroğ, Java + React bootcamp HRMS project front-end codes (React JS).

HRMS-front-end Engin Demiroğ, Java + React bootcamp HRMS project front-end codes. Spring Boot Back-end Project Job Advertisements Filters Pagination R

Apr 27, 2022
Twitter Clone built using React, Firebase, Material UI and React Flip Move.

Twitter Clone with ReactJS You can send a simple tweet in this app. Twitter Clone Demo Link click me for demo. Topics Material UI React Flip Move Auth

Apr 17, 2022
react-blog: a personal blog system written in React
react-blog: a personal blog system written in React

??️react-blog:用React写的一款个人博客系统,有博客展示页面和后台管理页面,可以管理文章、分类、标签、相册、说说、时间轴、留言、友链等信息。

May 9, 2022
State managment library for React and React Native apps

State managment library for React and React Native apps

May 3, 2022
A mobile-first React prototyping tool with React-Bootstrap component integration
A mobile-first React prototyping tool with React-Bootstrap component integration

A mobile-first React prototyping tool with React-Bootstrap component integration

Apr 17, 2022
This is a React application for managing books you want to read. It tracks your progress and makes the reading the books enlisted as a goal. The project is built using HTML, CSS, JavaScript, React, and Redux.
This is a React application for managing books you  want to read. It tracks your progress and makes the reading the books enlisted as a goal. The project is built using HTML, CSS, JavaScript, React, and Redux.

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

Oct 17, 2021
Online React Editor and IDE: compile, run, and host React apps
Online React Editor and IDE: compile, run, and host React apps

react-add-subtract Create a simple React application and learn how to manage the state and render JSX You need to provide an input which accept number

Oct 25, 2021
Web frontend of the NLW heat 2021 project, used: React; Typescript/TSX; Vite; Socket.io; Scss/Sass; and other libs for react (axios, context...).
Web frontend of the NLW heat 2021 project, used: React; Typescript/TSX; Vite; Socket.io; Scss/Sass; and other libs for react (axios, context...).

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...).

Oct 25, 2021
FullStack app containing an API (NestJS), a web app (React) and a mobile app (React Native).

Food Order This project is a workspace containing an API (NestJS), a web app (React) and a mobile app (React Native). Content Food Order Content Get S

Apr 24, 2022