
Company News
/Security News
Socket Selected for OpenAI's Cybersecurity Grant Program
Socket is an initial recipient of OpenAI's Cybersecurity Grant Program, which commits $10M in API credits to defenders securing open source software.
react-data-cache
Advanced tools
Lightweight React hook for caching, prefetching, and refetching data with stale-time control.
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.
npm install react-data-cache
# or
yarn add react-data-cache
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>
);
}
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>
);
}
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} />;
}
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>
);
}
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>
);
}
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>
);
}
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>
);
}
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
}
);
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);
The library provides pre-built adapters for common pagination patterns:
PaginationAdapters.offsetBased<Post>()
PaginationAdapters.cursorBased<Post>()
PaginationAdapters.linkBased<Post>()
PaginationAdapters.custom<Post>({
selectData: (response) => response.items,
getNext: (response, currentOffset = 0) => {
return response.hasMore ? currentOffset + response.items.length : undefined;
},
initialParam: 0
})
The library uses a centralized cache system with the following states:
const { data } = useData(
`user-${userId}`,
fetchUser,
{ enabled: !!userId }
);
const handleLike = () => {
updateOptimistically(
{ likes: data.likes + 1 },
async () => {
await fetch(`/api/posts/${postId}/like`, { method: 'POST' });
}
);
};
useEffect(() => {
const interval = setInterval(refetch, 60000); // Refetch every minute
return () => clearInterval(interval);
}, [refetch]);
useData OptionsstaleTime - How long data is considered fresh (default: 5 seconds)refetchOnMount - Whether to refetch when component mounts (default: false)noCache - Bypass cache entirely (default: false)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 functionbackgroundSync - Enable background sync (default: false)offlineSupport - Enable offline support (default: false)realtime - Enable real-time subscriptions (default: false)subscriptionUrl - WebSocket URL for real-time updatesonUpdate - Callback for real-time updatescacheStrategy - 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 metricsuseUniversalInfiniteQuery Optionsselect - Extract items from API responsegetNextPageParam - Determine next page parametergetPreviousPageParam - Determine previous page parameter (for bidirectional)initialPageParam - Initial page parametertransformPage - Transform each page response before storinghasNextPage - Check if there are more pages (fallback)staleTime - How long data is considered freshrefetchOnMount - Whether to refetch when component mountsenabled - Whether to enable the queryuseData are also availableSee the User Documentation for comprehensive examples including:
Common issues and solutions:
// Use appropriate stale time or manual refetch
const { data, refetch } = useData('key', fetchFn, {
staleTime: 0, // Always consider data stale
refetchOnMount: true
});
// Provide explicit types
const { data } = useData<User>('user', fetchUser);
The library handles request cancellation automatically with AbortController. Ensure proper cleanup in your fetch functions.
We welcome contributions! Please see our Contributing Guidelines and Developer Documentation for details.
MIT License - see LICENSE for details.
Built with ❤️ for the React community
FAQs
Lightweight React hook for caching, prefetching, and refetching data with stale-time control.
The npm package react-data-cache receives a total of 1 weekly downloads. As such, react-data-cache popularity was classified as not popular.
We found that react-data-cache demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
/Security News
Socket is an initial recipient of OpenAI's Cybersecurity Grant Program, which commits $10M in API credits to defenders securing open source software.

Security News
Socket CEO Feross Aboukhadijeh joins 10 Minutes or Less, a podcast by Ali Rohde, to discuss the recent surge in open source supply chain attacks.

Research
/Security News
Campaign of 108 extensions harvests identities, steals sessions, and adds backdoors to browsers, all tied to the same C2 infrastructure.