Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v9] feat(cache): promise based caching with workers #3245

Open
wants to merge 7 commits into
base: v9
Choose a base branch
from
Open

Conversation

krispya
Copy link
Member

@krispya krispya commented Apr 30, 2024

⚠️Includes breaking change: Removes extensions from useLoader in favor of passing in already extended loader instances.

Caching was hidden behind react-suspend and did not offer any way to utilize browser APIs or extend it. This PR adds PromiseCache class that creates a local cache compatible with React.use backed by the web Cache API. The Cache API offers many benefits such as:

  • Fully integrated with fetch.
  • Allows for caching resources across worker boundaries.
  • Allows for downloading, processing and caching resources inside of a service worker.
  • Allows for client side control over offline caching.
  • Fully inspectable via dev tools

The PromiseCache accesses and processes resources from the global cache while handling promises for you.

// Pass in the name of the global cache that backs it, or the cache itself if you have it.
const cache = new PromiseCache('assets') 
// Fetches resources from a url, caches it, then runs a handler on that data. Returns a promise with the handler's return value.
// ?? Should this just return a blob instead of a blob URL? ??
cache.run(url: string, handler: async (cacheUrl: string) => any)): Promise<any>
// Add a promise.
cache.add(url: string, promise: Promise<any>)
//  Get a promise from the cache.
cache.get(url: string): Promise<any>
// Check if the cache has a url stored.
cache.has(url: string): boolean
// Delete a cached response. Return true if successful, false if unsuccessful.
await cache.delete(url: string): Promise<boolean>

It looks like this when combined with React.use:

const cache = loaderCaches.get(loaderInstance)!

if (!cache.has(url)) cache.run(url, async (cacheUrl) => loadAsset(cacheUrl, loaderInstance))
const result = React.use(cache.get(url)!)

TODO

  • Testing is broken. jest doesn't mock the Cache API or fetch. Trying to polyfill either proved harder than I expected.
  • The API could use some tuning.
  • This should probably get split off into its own library.
  • Make compatible with Node.

Copy link

codesandbox-ci bot commented Apr 30, 2024

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 5e53956:

Sandbox Source
example Configuration

@krispya
Copy link
Member Author

krispya commented May 2, 2024

  • extensions removed from useLoader.
  • Testing now works. Requires Node 18 or higher since it is using Node's browser-compliant fetch API.

@krispya krispya mentioned this pull request May 2, 2024
18 tasks
@krispya krispya changed the title feat(cache): promise based caching with workers [v9] feat(cache): promise based caching with workers May 3, 2024
@krispya
Copy link
Member Author

krispya commented May 3, 2024

@CodyJasonBennett How can I check if this PR fixes the issue trying to be solved by #3243 ?

@CodyJasonBennett CodyJasonBennett changed the title [v9] feat(cache): promise based caching with workers [v9] feat(cache)!: promise based caching with workers May 3, 2024
@CodyJasonBennett CodyJasonBennett changed the title [v9] feat(cache)!: promise based caching with workers [v9] feat(cache): promise based caching with workers May 3, 2024
@CodyJasonBennett
Copy link
Member

I'll add tests once we can reproduce issues with error boundaries also. I don't expect that to be addressed here.

Comment on lines -113 to -114
// Apply loader extensions
if (extensions) extensions(loader)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to keep this and deopt calls to useLoader with extensions to only cache for the caller component? Can pass React.useId to discern the component.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants