JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 22
  • Score
    100M100P100Q91328F
  • License MIT

A smart React data-fetching hook with caching, retries, TTL, and auto token refresh.

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 endpoints
  • token: initial token (if you already have one in memory/storage)
  • refreshToken: a function returning a new token (or null if refresh fails) — this will be automatically used when a request gets a 401
  • retryLimit: 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 refetch function 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, dependencies etc] (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 refetch when 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)
  • Example: after creating a new user, you might refetch /users.

3. Token handling

Since the library handles auto‐refresh for 401s:

  • Ensure your refreshToken logic 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 skip or 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 error state, 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, dependencies etc), 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)