npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

msc-api-caller-react

v1.1.20

Published

A lightweight React data fetching library inspired by TanStack Query but minimal in scope. Provides simple hooks for API calls with built-in loading states, error handling, and intelligent caching.

Readme

React Microquery

A lightweight React data fetching library inspired by TanStack Query but minimal in scope. Provides simple hooks for API calls with built-in loading states, error handling, and intelligent caching.

Features

  • 🚀 Lightweight: ~4KB minified, minimal footprint
  • ⚛️ React Hooks: Built with modern React patterns
  • 🛡️ TypeScript: Full type safety and inference
  • 🔄 Global State: Context-based error and loading management
  • 📦 Dual Modules: ESM and CommonJS support
  • 🎯 Simple API: Easy to learn and use
  • 💾 Smart Caching: Built-in cache with TTL support
  • 🚫 Deduplication: Prevents duplicate API calls
  • 🧹 Cache Management: Full control over cache operations
  • 🔗 Cross-Component Sync: Automatic data synchronization between components

Installation

npm install msc-api-caller-react
# or
yarn add msc-api-caller-react
# or
pnpm add msc-api-caller-react

Quick Start

import {
  useApiCaller,
  useLazyApiCaller,
  APICallerProvider,
  useCacheManager,
} from 'msc-api-caller-react';
import { useState } from 'react';

// Example API function
const fetchUser = async (id: number) => {
  const response = await fetch(`https://api.example.com/users/${id}`);
  return response.json();
};

function App() {
  return (
    <APICallerProvider
      value={{
        onError: error => console.error('Global error:', error),
        setGlobalLoading: loading => console.log('Global loading:', loading),
        debug: true, // Enable debug logging
      }}
    >
      <UserProfile userId={1} />
    </APICallerProvider>
  );
}

// Automatic fetching on mount
function UserProfile({ userId }: { userId: number }) {
  const { data, isLoading, fetch } = useApiCaller(fetchUser, {
    args: [userId],
    storeKey: 'user-data', // Enable caching
    onSuccess: data => console.log('User loaded:', data),
    onError: error => console.error('Failed to load user:', error),
  });

  if (isLoading) return <div>Loading...</div>;

  return (
    <div>
      <h1>{data?.name}</h1>
      <p>{data?.email}</p>
      <button onClick={() => fetch(userId)}>Refresh</button>
    </div>
  );
}

// Manual triggering
function SearchUser() {
  const [search, setSearch] = useState('');
  const [fetchUser, { data, isLoading }] = useLazyApiCaller(fetchUser, {
    storeKey: 'search-user',
  });

  const handleSearch = async () => {
    try {
      await fetchUser(parseInt(search));
    } catch (error) {
      console.error('Search failed:', error);
    }
  };

  return (
    <div>
      <input
        value={search}
        onChange={e => setSearch(e.target.value)}
        placeholder="Enter user ID"
      />
      <button onClick={handleSearch} disabled={isLoading}>
        {isLoading ? 'Searching...' : 'Search'}
      </button>
      {data && <div>Found: {data.name}</div>}
    </div>
  );
}

// Cache management
function CacheControls() {
  const { clearCache, removeCache } = useCacheManager();

  return (
    <div>
      <button onClick={() => clearCache()}>Clear All Cache</button>
      <button onClick={() => removeCache('user-data_123')}>
        Remove Specific Cache
      </button>
    </div>
  );
}

API Reference

useApiCaller(api, config)

Automatically fetches data when component mounts (unless skip: true).

Parameters:

  • api: Function - The async function to call
  • config: ApiConfig - Configuration object (optional)

Returns:

{
  data: Result | null,    // The fetched data
  isLoading: boolean,      // Loading state
  fetch: Function         // Manual fetch function
}

useLazyApiCaller(api, config)

Returns a fetch function that can be called manually. Does not fetch automatically.

Parameters:

  • api: Function - The async function to call
  • config: ApiConfig - Configuration object (optional)

Returns:

[
  fetch: Function,        // Manual fetch function
  {
    data: Result | null,  // The fetched data
    isLoading: boolean,    // Loading state
  }
]

useCacheManager()

Provides utilities for managing the cache.

Returns:

{
  clearCache: () => void;           // Clear all cache entries
  removeCache: (key: string) => void; // Remove cache entry by exact key
}

APICallerProvider

React context provider for global error and loading state management.

Props:

interface ApiCallerConfig {
  onError?: (error: Error) => void;
  setGlobalLoading?: (loading: boolean) => void;
  debug?: boolean;
}

ApiConfig

interface ApiConfig<TApi extends FunctionType> {
  args?: Parameters<TApi>; // Arguments to pass to API function
  onSuccess?: (data, ...args) => void; // Success callback
  onError?: (error: Error) => void; // Error callback
  onSettled?: () => void; // Called after fetch completes
  globalLoading?: boolean; // Use global loading state instead of local
  skip?: boolean; // Skip automatic fetching
  storeKey?: string; // Cache key for storage
  cacheConfig?: CacheConfig; // Custom cache configuration
}

CacheConfig

interface CacheConfig {
  ttl?: number; // Time to live in milliseconds (default: 5 minutes)
  maxSize?: number; // Maximum number of cache entries (global setting)
}

Caching System

React Microquery includes a sophisticated caching system:

Features

  • TTL Support: Cache entries expire after specified time
  • Deduplication: Prevents duplicate API calls for the same request
  • Cache Keys: Intelligent cache key generation based on function and parameters
  • Size Management: Automatic cache size management with LRU eviction
  • Manual Control: Full control over cache operations

Cache Key Format

Cache keys are generated as: {storeKey}_{param1}_{param2}_...

Example Cache Usage

const { data } = useApiCaller(fetchUser, {
  args: [123],
  storeKey: 'user-profile', // Cache key will be: 'user-profile_123'
  cacheConfig: {
    ttl: 10 * 60 * 1000, // 10 minutes
  },
});

Comparison with TanStack Query

| Feature | React Microquery | TanStack Query | | --------------------- | ---------------- | -------------- | | Bundle Size | ~4KB | ~13KB | | Caching | ✅ | ✅ | | Background Refetching | ❌ | ✅ | | Infinite Queries | ❌ | ✅ | | Mutations | ❌ | ✅ | | Devtools | ❌ | ✅ | | TypeScript | ✅ | ✅ | | Global State | ✅ | ✅ | | Error Handling | ✅ | ✅ | | Deduplication | ✅ | ✅ | | Cache Management | ✅ | ✅ |

React Microquery is designed for simple use cases where you need basic data fetching with caching and loading states, without the complexity of background refetching and advanced features.

Cross-Component Synchronization

One of the powerful features of React Microquery is automatic data synchronization between components. When multiple components use the same cache key, they automatically stay synchronized when data changes.

How it works

When Component A and Component B use the same storeKey and parameters, they share the same cache entry. When either component fetches new data, all components using that cache key automatically receive the updated data and re-render.

Example

// Component A - Displays user data automatically
const UserProfile = ({ userId }: { userId: number }) => {
  const { data, isLoading } = useApiCaller(fetchUser, {
    args: [userId],
    storeKey: 'user-profile', // Same cache key
  });

  if (isLoading) return <div>Loading...</div>;

  return (
    <div>
      <h1>{data?.name}</h1>
      <p>{data?.email}</p>
    </div>
  );
};

// Component B - Can refresh user data
const UserActions = ({ userId }: { userId: number }) => {
  const [refreshUser] = useLazyApiCaller(fetchUser, {
    storeKey: 'user-profile', // Same cache key
  });

  const handleRefresh = async () => {
    await refreshUser(userId);
    // UserProfile component will automatically update!
  };

  return <button onClick={handleRefresh}>Refresh User</button>;
};

function App() {
  return (
    <div>
      <UserProfile userId={1} />
      <UserActions userId={1} />
    </div>
  );
}

Cache Key Format

Cache keys are generated as: {storeKey}_{param1}_{param2}_...

For the example above, both components use:

  • storeKey: 'user-profile'
  • args: [1]

This creates the cache key: 'user-profile_1'

Both components subscribe to changes for this key and automatically update when the data changes.

Advanced Usage

Error Handling

const MyComponent = () => {
  const { data, isLoading, error } = useApiCaller(fetchData, {
    onError: err => {
      // Local error handling
      console.error('API Error:', err);
    },
  });

  if (error) {
    return <div>Error: {error.message}</div>;
  }

  // Component content...
};

Global Loading State

function App() {
  const [globalLoading, setGlobalLoading] = useState(false);

  return (
    <APICallerProvider
      value={{
        setGlobalLoading,
        onError: error => console.error('API Error:', error),
      }}
    >
      <MyComponent />
      {globalLoading && <GlobalLoadingSpinner />}
    </APICallerProvider>
  );
}

function MyComponent() {
  const { isLoading } = useApiCaller(fetchData, {
    globalLoading: true, // Use global loading instead of local
  });

  // Component content...
}

Cache Management

function CacheExample() {
  const { clearCache, removeCache } = useCacheManager();
  const { data, fetch } = useApiCaller(fetchUser, {
    args: [123],
    storeKey: 'user-data',
  });

  const handleRefresh = async () => {
    // Remove specific cache entry before refetching
    removeCache('user-data_123');
    await fetch(123);
  };

  const handleClearAll = () => {
    // Clear all cached data
    clearCache();
  };

  return (
    <div>
      <button onClick={handleRefresh}>Refresh User</button>
      <button onClick={handleClearAll}>Clear All Cache</button>
      {/* Component content */}
    </div>
  );
}

Debug Mode

Enable debug logging to see detailed information about API calls, cache hits, and more:

<APICallerProvider value={{ debug: true }}>
  <App />
</APICallerProvider>

Debug output includes:

  • Cache hits and misses
  • API call initiation and completion
  • Error details
  • Cache key generation

🔧 Troubleshooting

Hooks Order Issues

When using React Microquery with React Compiler, you may encounter hooks order errors:

hook.js:608 React has detected a change in the order of Hooks called by null. This will lead to bugs and errors if not fixed.

Solution:

Add 'use no memo'; directive at the top of the component file:

'use no memo';

import { useApiCaller } from 'msc-api-caller-react';

const MyComponent = () => {
  const { data, isLoading } = useApiCaller(fetchData);
  // Component content...
};

Common Issues

  1. Cache Not Working: Ensure storeKey is provided in the config
  2. Duplicate Requests: Check if different storeKey values are being used for the same data
  3. Global Loading Not Working: Ensure setGlobalLoading is provided to APICallerProvider
  4. TypeScript Errors: Make sure your API functions return properly typed promises

TypeScript Support

React Microquery is built with TypeScript and provides excellent type inference:

// API function with proper typing
const fetchUser = async (id: number): Promise<User> => {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
};

// Hook usage with full type inference
const { data } = useApiCaller(fetchUser, { args: [123] });
// data is automatically typed as User | null

Contributing

We welcome contributions! Please feel free to submit a Pull Request.

License

MIT © [Your Name]