Case Study: Speeding Up Dashboard Performance in React with SWR Caching
When building enterprise-grade React applications, performance issues often trace back to how data is loaded. Standard patterns introduce jarring loading indicators, disrupt scroll positions, and trigger excessive server queries. Modern web design demands zero-latency navigation. If a user navigates away from a tab and returns, the UI should immediately render stale data while silently fetching fresh results in the background.
To solve this, we designed useResource—the newest feature in our open-source utility suite, react-hook-lab. Below, we'll walk through how this SWR-based architecture, alongside performance improvements made directly to the useIndexedDB hook, makes it easy to build fluid, robust UIs that gracefully transition online and offline.
The Architecture of useResource
Instead of managing local state, cache persistence, and server validation across separate, disjointed contexts, useResource registers hooks globally within an internal controller manager. It manages garbage collection, dedupes parallel requests, and interfaces directly with the browser's IndexedDB storage.
To facilitate this, we added an enabled configuration parameter to useIndexedDB. By doing so, the cache manager can turn storage operations on or off depending on the state of the parent hook, resolving race conditions and reducing memory footprints.
Let's check out how to implement this pattern across common application components.
Implementation Code Examples
1. Seamless SWR Caching
By defining our data dependencies declaratively with a unique key, we can safely share cache states across completely disconnected page components without invoking context providers.
import React from 'react';
import { useResource } from 'react-hook-lab';
const getMarketRates = async (signal) => {
const res = await fetch('https://api.coincap.io/v2/assets', { signal });
const json = await res.json();
return json.data;
};
export function MarketDashboard() {
const { data, loading, error } = useResource({
key: 'crypto-rates',
fetcher: getMarketRates,
staleTime: 5000,
});
if (loading && !data) return <p>Fetching active rates...</p>;
if (error) return <p>Failed to load data: {error.message}</p>;
return (
<ul>
{data?.slice(0, 5).map((coin) => (
<li key={coin.id}>{coin.name}: ${parseFloat(coin.priceUsd).toFixed(2)}</li>
))}
</ul>
);
}
2. Persistent Offline Storage with Optimistic Editing
For critical data like configurations or checklists, utilizing local persistence guarantees your users can launch the application instantly even under poor network conditions.
import React from 'react';
import { useResource } from 'react-hook-lab';
const saveConfigSettings = async (signal) => {
const res = await fetch('/api/settings', { signal });
return res.json();
};
export function UserSettings() {
const { data: config, mutate } = useResource({
key: 'app-settings',
fetcher: saveConfigSettings,
cache: 'indexeddb',
persist: { store: 'settings-store' },
initialData: { theme: 'light', notifications: true },
});
const toggleTheme = () => {
mutate((prev) => ({
...prev,
theme: prev?.theme === 'light' ? 'dark' : 'light'
}));
};
return (
<div style={{ background: config?.theme === 'dark' ? '#333' : '#fff' }}>
<p>Current Theme: {config?.theme}</p>
<button onClick={toggleTheme}>Toggle Interface Theme</button>
</div>
);
}
Resources
- GitHub Repository: https://github.com/Saurav-TB-Pandey/react-hook-lab
- NPM Package: https://www.npmjs.com/package/react-hook-lab
- LinkedIn Profile: https://www.linkedin.com/in/pandeysaurav/
Comments
Post a Comment