function useAsyncRateLimiter<TFn, TSelected>(
fn,
options,
selector?): ReactAsyncRateLimiter<TFn, TSelected>
function useAsyncRateLimiter<TFn, TSelected>(
fn,
options,
selector?): ReactAsyncRateLimiter<TFn, TSelected>
Defined in: react-pacer/src/async-rate-limiter/useAsyncRateLimiter.ts:170
A low-level React hook that creates an AsyncRateLimiter instance to limit how many times an async function can execute within a time window.
This hook is designed to be flexible and state-management agnostic - it simply returns a rate limiter instance that you can integrate with any state management solution (useState, Redux, Zustand, Jotai, etc).
Rate limiting allows an async function to execute up to a specified limit within a time window, then blocks subsequent calls until the window passes. This is useful for respecting API rate limits, managing resource constraints, or controlling bursts of async operations.
Unlike the non-async RateLimiter, this async version supports returning values from the rate-limited function, making it ideal for API calls and other async operations where you want the result of the maybeExecute call instead of setting the result on a state variable from within the rate-limited function.
The rate limiter supports two types of windows:
Error Handling:
The hook uses TanStack Store for reactive state management. The selector parameter allows you to specify which state changes will trigger a re-render, optimizing performance by preventing unnecessary re-renders when irrelevant state changes occur.
By default, all state changes will trigger a re-render. To optimize performance, you can provide a selector function that returns only the specific state values your component needs. The component will only re-render when the selected values change.
Available state properties:
• TFn extends AnyAsyncFunction
• TSelected = AsyncRateLimiterState<TFn>
TFn
AsyncRateLimiterOptions<TFn>
(state) => TSelected
ReactAsyncRateLimiter<TFn, TSelected>
// Basic API call rate limiting with return value - re-renders on any state change
const asyncRateLimiter = useAsyncRateLimiter(
async (id: string) => {
const data = await api.fetchData(id);
return data; // Return value is preserved
},
{ limit: 5, window: 1000 } // 5 calls per second
);
// Only re-render when execution state changes (optimized for loading indicators)
const asyncRateLimiter = useAsyncRateLimiter(
async (id: string) => {
const data = await api.fetchData(id);
return data;
},
{ limit: 5, window: 1000 },
(state) => ({ isExecuting: state.isExecuting })
);
// Only re-render when results are available (optimized for data display)
const asyncRateLimiter = useAsyncRateLimiter(
async (id: string) => {
const data = await api.fetchData(id);
return data;
},
{ limit: 5, window: 1000 },
(state) => ({
lastResult: state.lastResult,
successCount: state.successCount
})
);
// Only re-render when error/rejection state changes (optimized for error handling)
const asyncRateLimiter = useAsyncRateLimiter(
async (id: string) => {
const data = await api.fetchData(id);
return data;
},
{
limit: 5,
window: 1000,
onError: (error) => console.error('API call failed:', error),
onReject: (rateLimiter) => console.log('Rate limit exceeded')
},
(state) => ({
errorCount: state.errorCount,
rejectionCount: state.rejectionCount
})
);
// Only re-render when execution metrics change (optimized for stats display)
const asyncRateLimiter = useAsyncRateLimiter(
async (id: string) => {
const data = await api.fetchData(id);
return data;
},
{ limit: 5, window: 1000 },
(state) => ({
successCount: state.successCount,
errorCount: state.errorCount,
settleCount: state.settleCount,
rejectionCount: state.rejectionCount
})
);
// Only re-render when execution times change (optimized for window calculations)
const asyncRateLimiter = useAsyncRateLimiter(
async (id: string) => {
const data = await api.fetchData(id);
return data;
},
{ limit: 5, window: 1000 },
(state) => ({ executionTimes: state.executionTimes })
);
// With state management and return value
const [data, setData] = useState(null);
const { maybeExecute, state } = useAsyncRateLimiter(
async (query) => {
const result = await searchAPI(query);
setData(result);
return result; // Return value can be used by the caller
},
{
limit: 10,
window: 60000, // 10 calls per minute
onReject: (rateLimiter) => {
console.log(`Rate limit exceeded. Try again in ${rateLimiter.getMsUntilNextWindow()}ms`);
},
onError: (error) => {
console.error('API call failed:', error);
}
}
);
// Access the selected state
const { isExecuting, lastResult, rejectionCount } = state;
// Basic API call rate limiting with return value - re-renders on any state change
const asyncRateLimiter = useAsyncRateLimiter(
async (id: string) => {
const data = await api.fetchData(id);
return data; // Return value is preserved
},
{ limit: 5, window: 1000 } // 5 calls per second
);
// Only re-render when execution state changes (optimized for loading indicators)
const asyncRateLimiter = useAsyncRateLimiter(
async (id: string) => {
const data = await api.fetchData(id);
return data;
},
{ limit: 5, window: 1000 },
(state) => ({ isExecuting: state.isExecuting })
);
// Only re-render when results are available (optimized for data display)
const asyncRateLimiter = useAsyncRateLimiter(
async (id: string) => {
const data = await api.fetchData(id);
return data;
},
{ limit: 5, window: 1000 },
(state) => ({
lastResult: state.lastResult,
successCount: state.successCount
})
);
// Only re-render when error/rejection state changes (optimized for error handling)
const asyncRateLimiter = useAsyncRateLimiter(
async (id: string) => {
const data = await api.fetchData(id);
return data;
},
{
limit: 5,
window: 1000,
onError: (error) => console.error('API call failed:', error),
onReject: (rateLimiter) => console.log('Rate limit exceeded')
},
(state) => ({
errorCount: state.errorCount,
rejectionCount: state.rejectionCount
})
);
// Only re-render when execution metrics change (optimized for stats display)
const asyncRateLimiter = useAsyncRateLimiter(
async (id: string) => {
const data = await api.fetchData(id);
return data;
},
{ limit: 5, window: 1000 },
(state) => ({
successCount: state.successCount,
errorCount: state.errorCount,
settleCount: state.settleCount,
rejectionCount: state.rejectionCount
})
);
// Only re-render when execution times change (optimized for window calculations)
const asyncRateLimiter = useAsyncRateLimiter(
async (id: string) => {
const data = await api.fetchData(id);
return data;
},
{ limit: 5, window: 1000 },
(state) => ({ executionTimes: state.executionTimes })
);
// With state management and return value
const [data, setData] = useState(null);
const { maybeExecute, state } = useAsyncRateLimiter(
async (query) => {
const result = await searchAPI(query);
setData(result);
return result; // Return value can be used by the caller
},
{
limit: 10,
window: 60000, // 10 calls per minute
onReject: (rateLimiter) => {
console.log(`Rate limit exceeded. Try again in ${rateLimiter.getMsUntilNextWindow()}ms`);
},
onError: (error) => {
console.error('API call failed:', error);
}
}
);
// Access the selected state
const { isExecuting, lastResult, rejectionCount } = state;
Your weekly dose of JavaScript news. Delivered every Monday to over 100,000 devs, for free.