npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-data-cache

v1.1.2

Published

Lightweight React hook for caching, prefetching, and refetching data with stale-time control.

Readme

React Data Cache Library

A lightweight, performant React library for data fetching with built-in caching, prefetching, infinite scroll, and advanced features. Designed for modern React applications with TypeScript support.

🚀 Features

  • ⚡ Zero Configuration - Works out of the box with sensible defaults
  • 💾 Automatic Caching - Data is cached and shared across components
  • 🔄 Smart Refetching - Intelligent stale-time based refetching
  • 📱 Infinite Scroll - Universal pagination support for any API
  • 🎯 Prefetching - Proactive data loading for better UX
  • 🔒 Type Safety - Full TypeScript support with automatic type inference
  • ⚡ Performance - Efficient re-renders and request cancellation
  • 🌐 Universal - Works with any API structure and pagination pattern

🆕 NEW: Advanced Features

High Priority Enhancements

  • 🎯 Optimistic Updates - Instant UI updates with automatic rollback
  • 🔄 Advanced Retry Logic - Exponential backoff with custom error handling
  • 📱 Background Sync - Offline support with automatic synchronization

Medium Priority Enhancements

  • 🔌 Real-time Subscriptions - WebSocket integration for live updates
  • 💾 Advanced Caching - Multiple cache strategies (stale-while-revalidate, cache-first, etc.)
  • 📊 Performance Monitoring - Built-in metrics and analytics

📦 Installation

npm install react-data-cache
# or
yarn add react-data-cache

🎯 Quick Start

import { useData } from 'react-data-cache';

function UserProfile({ userId }: { userId: string }) {
  const { data, isLoading, error, refetch } = useData(
    `user-${userId}`,
    async (signal) => {
      const response = await fetch(`/api/users/${userId}`, { signal });
      return response.json();
    }
  );

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  
  return (
    <div>
      <h1>{data.name}</h1>
      <p>{data.email}</p>
      <button onClick={refetch}>Refresh</button>
    </div>
  );
}

🆕 Enhanced Usage Examples

1. Optimistic Updates with Rollback

function OptimisticPostUpdate({ postId }: { postId: string }) {
  const { data, updateOptimistically } = useData<Post>(
    `post-${postId}`,
    async (signal) => {
      const response = await fetch(`/api/posts/${postId}`, { signal });
      return response.json();
    },
    { optimisticUpdates: true }
  );

  const handleLike = () => {
    updateOptimistically(
      { likes: (data?.likes || 0) + 1 },
      async () => {
        await fetch(`/api/posts/${postId}/like`, { method: 'POST' });
      }
    );
  };

  return (
    <div>
      <h2>{data?.title}</h2>
      <button onClick={handleLike}>
        Like ({data?.likes || 0})
      </button>
    </div>
  );
}

2. Advanced Error Recovery and Retry Logic

function RobustDataFetch({ userId }: { userId: string }) {
  const { data, error, retry, retryCount, metrics } = useData<User>(
    `user-${userId}`,
    async (signal) => {
      const response = await fetch(`/api/users/${userId}`, { signal });
      if (!response.ok) throw new Error(`HTTP ${response.status}`);
      return response.json();
    },
    {
      retryAttempts: 5,
      retryDelay: 1000,
      exponentialBackoff: true,
      onError: (error, attempt) => {
        console.log(`Attempt ${attempt} failed:`, error.message);
      },
      enableMetrics: true
    }
  );

  if (error) {
    return (
      <div>
        <p>Failed to load user (attempt {retryCount})</p>
        <button onClick={retry}>Retry</button>
        <small>Fetch time: {metrics.fetchTime}ms</small>
      </div>
    );
  }

  return <UserCard user={data} />;
}

3. Background Sync and Offline Support

function OfflineAwarePosts() {
  const { data, syncStatus, metrics } = useData<Post[]>(
    'posts',
    async (signal) => {
      const response = await fetch('/api/posts', { signal });
      return response.json();
    },
    {
      backgroundSync: true,
      offlineSupport: true,
      staleTime: 30 * 1000
    }
  );

  return (
    <div>
      <div>Status: {syncStatus}</div>
      {data?.map(post => <PostCard key={post.id} post={post} />)}
    </div>
  );
}

4. Real-time Subscriptions

function LiveChat({ chatId }: { chatId: string }) {
  const { data, isConnected } = useData<Message[]>(
    `chat-${chatId}`,
    async (signal) => {
      const response = await fetch(`/api/chats/${chatId}/messages`, { signal });
      return response.json();
    },
    {
      realtime: true,
      subscriptionUrl: `ws://api.example.com/chats/${chatId}`,
      onUpdate: (newData) => {
        console.log('New message received:', newData);
      }
    }
  );

  return (
    <div>
      <div>{isConnected ? '🟢 Connected' : '🔴 Disconnected'}</div>
      {data?.map(message => <MessageBubble key={message.id} message={message} />)}
    </div>
  );
}

5. Advanced Caching Strategies

function SmartUserProfile({ userId }: { userId: string }) {
  const { data, metrics } = useData<User>(
    `user-${userId}`,
    async (signal) => {
      const response = await fetch(`/api/users/${userId}`, { signal });
      return response.json();
    },
    {
      cacheStrategy: "stale-while-revalidate",
      cacheTime: 10 * 60 * 1000, // 10 minutes
      backgroundRefetch: true,
      enableMetrics: true
    }
  );

  return (
    <div>
      <UserCard user={data} />
      <small>Cache hit rate: {(metrics.cacheHitRate * 100).toFixed(1)}%</small>
    </div>
  );
}

6. Performance Monitoring

function MonitoredDataFetch() {
  const { data, metrics } = useData<Post[]>(
    'monitored-posts',
    async (signal) => {
      const response = await fetch('/api/posts', { signal });
      return response.json();
    },
    {
      enableMetrics: true,
      onMetrics: (metrics) => {
        // Send to analytics service
        analytics.track('data_fetch_metrics', metrics);
      }
    }
  );

  return (
    <div>
      <div>Fetch Time: {metrics.fetchTime}ms</div>
      <div>Cache Hit Rate: {(metrics.cacheHitRate * 100).toFixed(1)}%</div>
      {data?.map(post => <PostCard key={post.id} post={post} />)}
    </div>
  );
}

📚 Documentation

🔧 Core API

useData<T>(key, fn, options?)

Main data fetching hook with caching and enhancements.

const { 
  data, 
  isLoading, 
  error, 
  refetch,
  // High Priority Enhancements
  updateOptimistically,
  retry,
  retryCount,
  syncStatus,
  // Medium Priority Enhancements
  isConnected,
  metrics
} = useData(
  'posts',
  async (signal) => {
    const response = await fetch('/api/posts', { signal });
    return response.json();
  },
  {
    // Standard options
    staleTime: 5 * 60 * 1000,
    refetchOnMount: true,
    
    // High Priority Enhancements
    optimisticUpdates: true,
    retryAttempts: 5,
    retryDelay: 1000,
    exponentialBackoff: true,
    backgroundSync: true,
    offlineSupport: true,
    
    // Medium Priority Enhancements
    realtime: true,
    subscriptionUrl: 'ws://api.example.com/updates',
    cacheStrategy: "stale-while-revalidate",
    cacheTime: 10 * 60 * 1000,
    backgroundRefetch: true,
    enableMetrics: true,
    onMetrics: (metrics) => console.log(metrics)
  }
);

useUniversalInfiniteQuery<TData, TResponse, TPageParam>(key, fetchFn, options)

Infinite scroll with universal pagination support and enhancements.

import { PaginationAdapters } from 'react-data-cache';

const {
  data,
  fetchNextPage,
  hasNextPage,
  isFetchingNextPage,
  // High Priority Enhancements
  updateOptimistically,
  retry,
  retryCount,
  syncStatus,
  // Medium Priority Enhancements
  isConnected,
  metrics
} = useUniversalInfiniteQuery(
  'posts',
  async (pageParam, signal) => {
    const response = await fetch(`/api/posts?page=${pageParam}`, { signal });
    return response.json();
  },
  {
    ...PaginationAdapters.offsetBased<Post>(),
    // All enhancement options available here too
    optimisticUpdates: true,
    retryAttempts: 3,
    backgroundSync: true,
    realtime: true,
    enableMetrics: true
  }
);

Enhanced Prefetching Utilities

import { 
  prefetchData, 
  prefetchMulti, 
  prefetchWithStrategy, 
  prefetchInBackground 
} from 'react-data-cache';

// Prefetch with retry logic
prefetchData('posts', fetchPosts, {
  retryConfig: {
    attempts: 3,
    delay: 1000,
    exponentialBackoff: true
  }
});

// Prefetch with cache strategy
prefetchWithStrategy('user-123', fetchUser, 'stale-while-revalidate');

// Background prefetching
const cleanup = prefetchInBackground('live-data', fetchLiveData, 30000);

🎨 Pagination Adapters

The library provides pre-built adapters for common pagination patterns:

Offset-Based Pagination

PaginationAdapters.offsetBased<Post>()

Cursor-Based Pagination

PaginationAdapters.cursorBased<Post>()

Link-Based Pagination

PaginationAdapters.linkBased<Post>()

Custom Adapter

PaginationAdapters.custom<Post>({
  selectData: (response) => response.items,
  getNext: (response, currentOffset = 0) => {
    return response.hasMore ? currentOffset + response.items.length : undefined;
  },
  initialParam: 0
})

🔄 State Management

The library uses a centralized cache system with the following states:

  • idle - Initial state, no data loaded
  • loading - Fetch in progress
  • success - Data loaded successfully
  • error - Fetch failed
  • isRefetching - Refetch in progress (preserves existing data)

⚡ Performance Features

  • Efficient Caching - Map-based cache with O(1) lookup
  • Request Cancellation - AbortController integration for performance
  • Event-Driven Updates - Cross-component state synchronization
  • Stale-Time Control - Intelligent refetching based on data age
  • Memory Management - Proper cleanup and leak prevention
  • Performance Monitoring - Built-in metrics and analytics
  • Advanced Caching - Multiple cache strategies for different use cases

🛠️ Advanced Usage

Conditional Fetching

const { data } = useData(
  `user-${userId}`,
  fetchUser,
  { enabled: !!userId }
);

Optimistic Updates

const handleLike = () => {
  updateOptimistically(
    { likes: data.likes + 1 },
    async () => {
      await fetch(`/api/posts/${postId}/like`, { method: 'POST' });
    }
  );
};

Background Refetching

useEffect(() => {
  const interval = setInterval(refetch, 60000); // Refetch every minute
  return () => clearInterval(interval);
}, [refetch]);

🔧 Configuration Options

useData Options

  • staleTime - How long data is considered fresh (default: 5 seconds)
  • refetchOnMount - Whether to refetch when component mounts (default: false)
  • noCache - Bypass cache entirely (default: false)

High Priority Enhancement Options

  • optimisticUpdates - Enable optimistic updates (default: false)
  • retryAttempts - Number of retry attempts (default: 0)
  • retryDelay - Delay between retries in ms (default: 1000)
  • exponentialBackoff - Use exponential backoff (default: true)
  • onError - Custom error handler function
  • backgroundSync - Enable background sync (default: false)
  • offlineSupport - Enable offline support (default: false)

Medium Priority Enhancement Options

  • realtime - Enable real-time subscriptions (default: false)
  • subscriptionUrl - WebSocket URL for real-time updates
  • onUpdate - Callback for real-time updates
  • cacheStrategy - Cache strategy ("default", "stale-while-revalidate", "cache-first", "network-first")
  • cacheTime - How long to cache data (default: 5 minutes)
  • backgroundRefetch - Enable background refetching (default: false)
  • enableMetrics - Enable performance monitoring (default: false)
  • onMetrics - Callback for performance metrics

useUniversalInfiniteQuery Options

  • select - Extract items from API response
  • getNextPageParam - Determine next page parameter
  • getPreviousPageParam - Determine previous page parameter (for bidirectional)
  • initialPageParam - Initial page parameter
  • transformPage - Transform each page response before storing
  • hasNextPage - Check if there are more pages (fallback)
  • staleTime - How long data is considered fresh
  • refetchOnMount - Whether to refetch when component mounts
  • enabled - Whether to enable the query
  • All enhancement options from useData are also available

🧪 Examples

See the User Documentation for comprehensive examples including:

  • Basic data fetching
  • Error handling
  • Infinite scroll implementations
  • Prefetching strategies
  • Performance optimization
  • Type safety examples
  • Complete application examples
  • NEW: Enhanced features examples

🔍 Troubleshooting

Common issues and solutions:

Data Not Updating

// Use appropriate stale time or manual refetch
const { data, refetch } = useData('key', fetchFn, {
  staleTime: 0, // Always consider data stale
  refetchOnMount: true
});

Type Errors

// Provide explicit types
const { data } = useData<User>('user', fetchUser);

Memory Leaks

The library handles request cancellation automatically with AbortController. Ensure proper cleanup in your fetch functions.

🤝 Contributing

We welcome contributions! Please see our Contributing Guidelines and Developer Documentation for details.

📄 License

MIT License - see LICENSE for details.

🆘 Support

🚀 Roadmap

  • [x] Optimistic updates with rollback
  • [x] Advanced error recovery and retry logic
  • [x] Background sync and offline support
  • [x] Real-time subscriptions
  • [x] Advanced caching strategies
  • [x] Performance monitoring
  • [ ] Request queuing and batching
  • [ ] Server-side rendering (SSR) support
  • [ ] GraphQL integration
  • [ ] Middleware system

Built with ❤️ for the React community