A basic cache for React applications designed for experimental features with suspense-based data fetching
npx @tessl/cli install tessl/npm-react-cache@2.0.0React Cache is a basic cache for React applications that provides suspense-based data fetching capabilities. It serves as a reference implementation for more advanced caching solutions and is designed to work alongside experimental React features.
⚠️ Experimental Package: This package is explicitly unstable and not recommended for production use. The API will change between versions.
import { unstable_createResource, unstable_setGlobalCacheLimit } from "react-cache";CommonJS:
const { unstable_createResource, unstable_setGlobalCacheLimit } = require("react-cache");import React, { Suspense } from "react";
import { unstable_createResource } from "react-cache";
// Create a resource for fetching user data
const UserResource = unstable_createResource(
async (userId) => {
const response = await fetch(`/api/users/${userId}`);
return response.json();
},
(userId) => `user-${userId}` // Optional hash function for cache keys
);
function UserProfile({ userId }) {
// This will suspend if data is not cached, or return cached data
const user = UserResource.read(userId);
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<UserProfile userId={123} />
</Suspense>
);
}React Cache is built around several key components:
Creates a cached resource that integrates with React Suspense for data fetching.
/**
* Creates a resource that can cache async data fetching results
* @param fetch - Function that returns a thenable (promise-like) for data fetching
* @param maybeHashInput - Optional hash function for generating cache keys (defaults to identity function)
* @returns Resource object with read and preload methods
*/
function unstable_createResource<I, K, V>(
fetch: (input: I) => Thenable<V>,
maybeHashInput?: (input: I) => K
): Resource<I, V>;
interface Resource<I, V> {
/**
* Reads cached data or suspends if data is not available
* Throws the promise if pending, throws error if rejected, returns value if resolved
*/
read(input: I): V;
/**
* Preloads data into cache without returning the value
* Useful for prefetching data before it's needed
*/
preload(input: I): void;
}Usage Example:
// Simple resource with identity hash function
const SimpleResource = unstable_createResource(async (id) => {
return fetch(`/api/data/${id}`).then(r => r.json());
});
// Resource with custom hash function for complex inputs
const ComplexResource = unstable_createResource(
async ({ userId, type, filters }) => {
const params = new URLSearchParams({ userId, type, ...filters });
return fetch(`/api/complex?${params}`).then(r => r.json());
},
({ userId, type, filters }) => `${userId}-${type}-${JSON.stringify(filters)}`
);
// Preload data before component renders
ComplexResource.preload({ userId: 123, type: 'profile', filters: { active: true } });Sets the global cache limit for all resources.
/**
* Sets the global cache limit for all resources
* @param limit - Maximum number of cached entries across all resources
*/
function unstable_setGlobalCacheLimit(limit: number): void;Usage Example:
import { unstable_setGlobalCacheLimit } from "react-cache";
// Set global cache to hold maximum 1000 entries
unstable_setGlobalCacheLimit(1000);
// Default limit is 500 entries/**
* Resource interface for cached data fetching
*/
interface Resource<I, V> {
read(input: I): V;
preload(input: I): void;
}/**
* Promise-like interface used by React Cache
*/
interface Thenable<T> {
then<U>(
onFulfill?: (value: T) => U | Thenable<U>,
onReject?: (error: any) => U | Thenable<U>
): Thenable<U>;
}/**
* Internal cache entry result types (for reference)
*/
type PendingResult = {
status: 0;
value: Thenable<any>; // The suspender thenable
};
type ResolvedResult<V> = {
status: 1;
value: V;
};
type RejectedResult = {
status: 2;
value: any; // The error
};
type Result<V> = PendingResult | ResolvedResult<V> | RejectedResult;React Cache integrates with React's error boundaries for error handling:
// Errors thrown by fetch functions will be re-thrown when read() is called
const ErrorProneResource = unstable_createResource(async (id) => {
const response = await fetch(`/api/data/${id}`);
if (!response.ok) {
throw new Error(`Failed to fetch: ${response.status}`);
}
return response.json();
});
function ErrorBoundary({ children }) {
return (
<React.ErrorBoundary
fallback={<div>Something went wrong!</div>}
onError={(error) => console.error('Cache error:', error)}
>
{children}
</React.ErrorBoundary>
);
}read() and preload() methods can only be called during React component render phaseresource.read() must be wrapped in a <Suspense> boundary