typescriptadvanced

Suspense Data Fetching Pattern

Use React Suspense for data fetching with resource caching, error boundaries, and streaming SSR.

typescript
import { Suspense } from 'react';

type Status = 'pending' | 'success' | 'error';

interface Resource<T> {
  read(): T;
}

function createResource<T>(promise: Promise<T>): Resource<T> {
  let status: Status = 'pending';
  let result: T;
  let error: Error;

  const suspender = promise.then(
    (data) => { status = 'success'; result = data; },
    (err) => { status = 'error'; error = err; },
  );

  return {
    read() {
      switch (status) {
        case 'pending': throw suspender;
        case 'error': throw error;
        case 'success': return result;
      }
    },
  };
}

// Cache factory
function createResourceCache<T>() {
  const cache = new Map<string, Resource<T>>();

  return {
    get(key: string, fetcher: () => Promise<T>): Resource<T> {
      if (!cache.has(key)) {
        cache.set(key, createResource(fetcher()));
      }
      return cache.get(key)!;
    },
    invalidate(key: string) { cache.delete(key); },
    clear() { cache.clear(); },
  };
}

// Usage
interface User { id: string; name: string; avatar: string }
const userCache = createResourceCache<User>();

function UserCard({ userId }: { userId: string }) {
  const resource = userCache.get(userId, () =>
    fetch(`/api/users/${userId}`).then(r => r.json()),
  );
  const user = resource.read(); // Suspends if pending, throws if error

  return (
    <div className="bg-[#111] border border-white/10 rounded-xl p-4 flex items-center gap-3">
      <img src={user.avatar} alt="" className="w-10 h-10 rounded-full" />
      <div>
        <p className="text-white font-medium">{user.name}</p>
        <p className="text-gray-400 text-sm">{user.id}</p>
      </div>
    </div>
  );
}

function App() {
  return (
    <Suspense fallback={<div className="animate-pulse bg-gray-800 h-20 rounded-xl" />}>
      <UserCard userId="user-1" />
    </Suspense>
  );
}

export { createResource, createResourceCache };

Use Cases

  • Suspense-first data loading
  • Streaming SSR data patterns
  • Resource caching layer

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.