Data fetching in modern React apps often forces a trade-off: either you build complex caching infrastructure, or you accept awkward layout shifts, loading spinners, and sluggish transitions. SWR (Stale-While-Revalidate) changed the game for in-memory fetching, but what if your app needs to work seamlessly offline, cache gigabytes of data securely, and coordinate state across browser tabs?
Today, I'm thrilled to announce the release of useResource, a highly optimized React hook designed to tackle SWR-based data fetching, cache persistence, and state management inside the react-hook-lab library. Along with this, we've updated useIndexedDB with conditional control to lay down a robust architecture for offline-first React systems.
Let's dive into how it works and how you can use it to build incredibly snappy user interfaces.
Why useResource?
Unlike traditional query libraries that require extensive configuration, useResource brings atomic SWR caching and browser-native persistence under one simple roof.
Key features include:
-
Flexible Caching Layers: Store cached data in-memory, synchronize it globally across instances using
sharedmemory, or save it permanently viaindexeddb. - Optimistic Mutations: Update your local UI instantly while the background syncing handles server reconciliations.
- Automatic Retries: Smart exponential backoff retry logic built right into the hook.
- Tab Synchronization: Changes inside one tab automatically replicate in others, minimizing redundant API requests.
Feature Deep Dive & Code Examples
To power these advanced capabilities, we also upgraded useIndexedDB by adding a custom enabled option. This allows hooks to conditionally bypass storage transactions when they are idle or during complex setup states, avoiding unnecessary database operations.
Here are two concrete examples showing how you can integrate these updates into your codebase today.
Example 1: Basic SWR Data Fetching
If you need simple background revalidation with robust loading states and easy refresh actions, a lightweight memory cache is all you need.
import React from 'react';
import { useResource } from 'react-hook-lab';
const fetchUserProfile = async (signal) => {
const response = await fetch('/api/user/profile', { signal });
if (!response.ok) throw new Error('Failed to load profile');
return response.json();
};
export function UserProfile() {
const { data, loading, error, refresh } = useResource({
key: 'user-profile',
fetcher: fetchUserProfile,
staleTime: 10000, // consider fresh for 10 seconds
});
if (loading && !data) return <p>Loading your profile...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h3>Welcome, {data?.name}!</h3>
<p>Email: {data?.email}</p>
<button onClick={refresh}>Force Revalidate</button>
</div>
);
}
Example 2: Offline-First Caching with IndexedDB & Optimistic UI
By leveraging the newly integrated IndexedDB storage backend, you can store heavy datasets on the user's hard drive and update the interface immediately while server calls complete in the background.
import React from 'react';
import { useResource } from 'react-hook-lab';
const fetchTodoList = async (signal) => {
const response = await fetch('/api/todos', { signal });
return response.json();
};
export function TodoManager() {
const { data: todos, mutate, loading } = useResource({
key: 'todo-items',
fetcher: fetchTodoList,
cache: 'indexeddb', // Persist data locally via IndexedDB
persist: {
store: 'app-cache-store',
},
initialData: [],
});
const handleToggleTodo = (todoId) => {
// Optimistically update the checklist locally
mutate((currentList) => {
return (currentList || []).map((todo) =>
todo.id === todoId ? { ...todo, completed: !todo.completed } : todo
);
});
};
return (
<div>
<h2>Your Tasks {loading && ' (Syncing with server...)'}</h2>
<ul>
{todos?.map((todo) => (
<li key={todo.id} style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
<label>
<input
type="checkbox"
checked={!!todo.completed}
onChange={() => handleToggleTodo(todo.id)}
/>
{todo.text}
</label>
</li>
))}
</ul>
</div>
);
}
Resources
- GitHub Repository: https://github.com/Saurav-TB-Pandey/react-hook-lab
- NPM Package: https://www.npmjs.com/package/react-hook-lab
- Connect with me on LinkedIn: https://www.linkedin.com/in/pandeysaurav/
Originally published on my blog. You can read the alternative breakdown here.
Top comments (0)