Package Exports
- use-fetch-smart
- use-fetch-smart/dist/index.js
- use-fetch-smart/dist/index.mjs
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (use-fetch-smart) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
use-fetch-smart
A smart React data-fetching library with caching, TTL, retry logic, token auto-refresh, and simple mutation hooks.
✨ Why use-fetch-smart?
Fetching data in React apps often ends up with repetitive boilerplate: loading states, error handling, caching, token refresh logic, retry logic, etc.
use-fetch-smart is designed to replace that boilerplate with a clean, declarative interface and handle many of the common patterns for you.
Key benefits:
- Smart GET hook with caching + Time-to-Live (TTL) expiry
- Mutation hooks (POST / PUT / DELETE) with minimal setup
- Automatic retry logic (for network failures or 5xx responses)
- Automatic token refresh when a 401 occurs, and request replay
- Single global provider to configure your fetch-layer (base URL, token, retry limits)
- Devtools panel to inspect cache, TTL, retry counts, etc
- TypeScript first: full typing support out of the box
In short: stop re-writing loading/error/spinner logic, stop manually managing cache/invalidations, stop writing token refresh logic over and over. Let the library handle the data layer; you focus on your UI and domain logic.
🚀 Installation
npm install use-fetch-smart
# or
yarn add use-fetch-smart🧩 Provider Setup
Wrap your React app (or the high-level section of it) with the FetchSmartProvider to configure the global data‐fetching environment. Example:
import React from 'react';
import { FetchSmartProvider, FetchSmartDevtools } from 'use-fetch-smart';
const refreshToken = async () => {
// Your logic to request a new token when current one expires (401).
return await fetch('/auth/refresh')
.then(r => r.json())
.then(x => x.token)
.catch(() => null);
};
const AppWrapper = () => (
<FetchSmartProvider
config={{
baseURL: 'https://api.example.com',
token: 'initial-token-if-known',
refreshToken, // function to call on 401
retryLimit: 3, // global retry limit for failed requests
// you may add other axios-style config if supported
}}
>
<YourApp />
<FetchSmartDevtools />
</FetchSmartProvider>
);
export default AppWrapper;What this config does:
baseURL: the base URL for your API endpointstoken: initial token (if you already have one in memory/storage)refreshToken: a function returning a new token (ornullif refresh fails) — this will be automatically used when a request gets a 401retryLimit: number of retry attempts for network/server errors
Once the provider is set up, all hooks (useGetSmart, usePostSmart, etc.) will automatically use this configured instance.
📊 Fetching Data — useGetSmart
import { useGetSmart } from 'use-fetch-smart';
const Users = () => {
const { data, loading, error, refetch } = useGetSmart<User[]>('/users', {
cacheTimeMs: 2 * 60 * 1000,
persist:true
});
if (loading) return <p>Loading…</p>;
if (error) return <p>Something went wrong</p>;
return (
<div>
<button onClick={refetch}>Refetch</button>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
};What’s happening:
- Immediately returns cached data (if still valid) → fast UI
- When TTL expires, automatically re-fetches the data
- Automatically retries on network/5xx errors (up to the configured retry limit)
- If a 401 error happens with token present, will call
refreshToken()and replay the request - The
refetchfunction lets you manually force a fresh fetch (also resets cache)
Options you can pass:
cacheTimeMs: how long to keep the cached data before expiry- [
other options if supported by your library: e.g., staleWhileRevalidate, skip, dependenciesetc] (Ensure you document all supported options and defaults)
✏️ Mutations — usePostSmart, usePutSmart, useDeleteSmart
usePostSmart
import { usePostSmart } from 'use-fetch-smart';
const CreateUser = () => {
const { mutate, loading, error } = usePostSmart<User, { name: string }>('/users');
const handleCreate = () => {
mutate({ name: 'Alice' })
.then(() => alert('User created!'))
.catch(err => console.error(err));
};
return (
<button onClick={handleCreate} disabled={loading}>
{loading ? 'Creating…' : 'Create User'}
</button>
);
};usePutSmart
import { usePutSmart } from 'use-fetch-smart';
const UpdateUser = ({ id }: { id: string }) => {
const { mutate, loading } = usePutSmart<User, { name: string }>(`/users/${id}`);
return (
<button onClick={() => mutate({ name: 'Updated Name' })} disabled={loading}>
{loading ? 'Updating…' : 'Update User'}
</button>
);
};useDeleteSmart
import { useDeleteSmart } from 'use-fetch-smart';
const DeleteUser = ({ id }: { id: string }) => {
const { mutate, loading } = useDeleteSmart<void>(`/users/${id}`);
return (
<button onClick={() => mutate()} disabled={loading}>
{loading ? 'Deleting…' : 'Delete User'}
</button>
);
};What these provide:
- Automatically handle loading and error state
- Use your configured fetch instance (with token, retry logic, etc)
- Simple API: you call
mutate, pass data (where applicable), and get promise results - You can optionally tie this into your cache invalidation (if you want to refresh a related GET)
🛠 DevTools
Inside your provider you can add:
<FetchSmartDevtools />This gives a panel (mini UI) that shows:
- current cached keys & values
- TTL remaining for each cache entry
- retry count for each request
- status codes, timestamps, etc
Use case: helps you debug whether your caching/TTL is working, whether token refresh is happening, whether retries are kicking in.
✅ Optimisations & Best Practices
1. Choose appropriate cache TTLs
- If data rarely changes (eg. user profile settings), you can set a long TTL (e.g., 10-30 minutes).
- If data changes frequently (eg. live feed or notifications), use a short TTL or disable caching.
- Use manual
refetchwhen you know something changed (e.g., after a mutation) so UI stays fresh.
2. Invalidate or refetch after mutations
After a
POST/PUT/DELETE, you often want to refresh related GET calls.You can either:
- call
refetch()manually on relevant hooks - or integrate a custom invalidation logic: e.g., clear cache key manually using
cacheManager(if your library exposes it)
- call
Example: after creating a new user, you might refetch
/users.
3. Token handling
Since the library handles auto‐refresh for 401s:
- Ensure your
refreshTokenlogic returns the new token (and persists it if you store it) - On refreshing token, subsequent calls will use the new token automatically
- Edge case: if refresh fails (returns null), you may want to redirect to login/logout.
4. Minimise over-fetching
- Use
skipor conditional fetches (if supported) when component shouldn’t fetch on mount (e.g., waiting user input) - Use
dependencies(if supported) to fetch only when necessary.
5. Server-side data and hydration (if e.g. using Next.js)
- If you’re using SSR/SSG, you may want to pre‐fetch data and hydrate the cache on the client. Check if your library supports that.
- While this library focuses on client‐side React, you may integrate with SSR setups by using initial cache values or disabling caching on initial load.
6. Bundling & tree-shaking
- Since this library is lightweight and TypeScript‐first, ensure your bundler (e.g., Webpack/Rollup) tree-shakes unused parts.
- Avoid importing full library if you only use one hook. (i.e.,
import { useGetSmart } from 'use-fetch-smart'). - Monitor bundle size (e.g., via
bundlephobia).
7. Error boundaries and global error handling
- While hooks provide
errorstate, you might use a global error boundary or React error boundary to catch unexpected exceptions. - Use logging (e.g., Sentry) for network/fetch errors to track in production.
📋 API Reference
FetchSmartProvider
Props:
config: { baseURL: string; token?: string; refreshToken?: () => Promise<string | null>; retryLimit?: number; /* plus any axios config like headers, timeout etc */ }- Children: your React tree
- Optionally include
<FetchSmartDevtools />as a child.
Hooks
useGetSmart<T>(url: string, options?: { cacheTimeMs?: number; skip?: boolean; dependencies?: any[]; /* other possible options */ }) => { data: T | undefined; loading: boolean; error: Error | null; refetch: () => Promise<void> }
usePostSmart<T, P>(url: string) => { mutate: (payload: P) => Promise<T>; loading: boolean; error: Error | null }
usePutSmart<T, P>(url: string) => { mutate: (payload: P) => Promise<T>; loading: boolean; error: Error | null }
useDeleteSmart<T>(url: string) => { mutate: () => Promise<T>; loading: boolean; error: Error | null }
Other exports
axiosInstance— underlying Axios instance (for advanced use)cacheManager— to programmatically inspect/clear cache (if supported)setGlobalToken— to set token globally outside hooks (if you update token elsewhere)
(Make sure to list any more utility methods your library provides.)
🧠 Why This Library Is Better
- Less boilerplate: you get data, loading, error, refetch in one line instead of writing your own state and effect every time.
- Caching built-in: You don’t need separate libraries for caching; TTL is built in and automatic.
- Retry logic: Out‐of‐the-box support for retrying failed requests, helping stability in flaky network conditions.
- Token refresh built-in: No need to write the same “if 401 then refresh token and retry” logic everywhere — the provider handles it.
- Single global config: You configure once (base URL, token, retry, etc) and all hooks inherit it.
- Devtools for visibility: Monitoring cache, TTL, retries, status codes gives you better insight during development.
- TypeScript support: Helps with correctness, developer experience and future maintenance.
- Focused & lightweight: Not a full state-management or data layer library (like React Query), but a simpler, focused solution for fetch + cache + retry + token. If you don’t need all features of a heavier library this may be a perfect fit.
🧪 Example Full Workflow
// index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { FetchSmartProvider, FetchSmartDevtools } from 'use-fetch-smart';
const refreshToken = async () => {
const result = await fetch('/auth/refresh').then(r => r.json());
if (result.token) {
return result.token;
}
return null;
};
ReactDOM.render(
<FetchSmartProvider config={{ baseURL: 'https://api.myapp.com', token: localStorage.getItem('token') ?? undefined, refreshToken, retryLimit: 2 }}>
<App />
<FetchSmartDevtools />
</FetchSmartProvider>,
document.getElementById('root'),
);
// App.tsx
import React from 'react';
import { useGetSmart } from 'use-fetch-smart';
const Dashboard = () => {
const { data: users, loading, error, refetch } = useGetSmart<User[]>('/users', { cacheTimeMs: 5 * 60 * 1000 });
if (loading) return <div>Loading users …</div>;
if (error) return <div>Error loading users: {error.message}</div>;
return (
<div>
<h1>Users</h1>
<button onClick={refetch}>Refresh</button>
<ul>
{users?.map(u => <li key={u.id}>{u.name}</li>)}
</ul>
</div>
);
};
export default Dashboard;📦 Package Details & Stability
- Version: (update to latest)
- TypeScript definitions included
- MIT License
- Compatible with React 17+ (verify your supported versions)
- Lightweight bundle size (check on bundlephobia or similar)
👥 Contributing
If you’d like to contribute:
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-feature) - Write tests where applicable
- Follow the code style in the repo
- Submit a pull request
- Issues and feature suggestions are welcome
📝 Changelog
Please refer to CHANGELOG.md in the repo for full version history and breaking changes.
🎉 Thank You
If you like use-fetch-smart, please star the GitHub repo — your support helps a lot ❤️
Happy fetching.
✅ Checklist for You
- Update version number and npm badge if needed
- Add links/badges for npm, bundle size, CI, license
- If there are additional options for hooks (e.g.,
skip,dependenciesetc), document them clearly - Provide example images or gifs (if you have) for cache hits, retry logic, token refresh, devtools
- Provide a section for migration (if you are replacing an older version)
- Ensure the README is published with the package (npm will show it)