Skip to content

Add built-in Suspense cache with support for invalidation (refreshing)#20456

Merged
acdlite merged 30 commits intofacebook:masterfrom
acdlite:cache
Dec 18, 2020
Merged

Add built-in Suspense cache with support for invalidation (refreshing)#20456
acdlite merged 30 commits intofacebook:masterfrom
acdlite:cache

Conversation

@acdlite
Copy link
Copy Markdown
Collaborator

@acdlite acdlite commented Dec 14, 2020

Includes all the basic functionality. I think the only features we've discussed that are missing are 1) passing initial data to the refresh method (Update: done), and 2) providing an AbortSignal to cancel remaining requests on completion.

I can open multiple PRs if we want to land the features incrementally. I split it into atomic commits for this purpose, and so it's easier to review.

Most of the commits are already pretty focused, except the last one. I'll work on that.

Technical details

A naive version of this can be built in userspace using the state and context hooks. The internal implementation is nearly identical, but it has two special features that have no userspace equivalent:

Retaining in-progress caches across pings

All the data that loaded as a result of a single transition/update should share the same cache. This includes nested content that gets progressively "filled in" after the initial shell is displayed.

If the shell itself were wrapped in a Cache boundary, such that the cache can commit with suspending, then this is easy: once the boundary mounts, the cache is attached the React tree.

The tricky part is when the shell does not include a cache boundary. In the naive approach, since the cache is not part of the initial tree, it does not get retained; during the retry, a fresh cache is created, leading to duplicate requests and possibly an infinite loop as requests are endlessly created then discarded.

This is the essential problem we faced several years ago when building Simple Cache Provider (later the react-cache package).

Our solution is to retain in-flight caches on the root, associated by lane. The cache cleared from the root once all of the lanes that depend on it finish rendering. [Edit: For now, we're only tracking a single "pooled" cache. We'll add the per-lane map after some planned refactors to the lanes model. Though we still track the lanes that depend on the pooled cache, so that we know when it's OK to clear it.]

Propagating the cache context across spawned tasks

The other tricky bit is that once the initial shell commits, we spawn new tasks to fill in the remaining content. These tasks must use the same cache as the original transition. To implement this, when a Suspense fallback commits, we save a reference to the cache that was used during the attempt to render the primary children.

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed React Core Team Opened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants