@adetolla/react-idempo
v1.0.1
Published
A React idempotency helper for preventing duplicate API submissions and making retry-safe requests.
Maintainers
Readme
@adetolla/react-idempo
A React idempotency helper for preventing duplicate API submissions and making retry-safe requests.
Features
- Duplicate Prevention: Prevents double-clicks and concurrent form submissions while a request is pending.
- Idempotency Keys: Automatically generates, stores, and attaches UUID v4 idempotency keys to your requests.
- Retry-Safe: Reuses the same idempotency key for retries if a request fails (e.g. due to network errors).
- Auto-Rotation: Generates a new key automatically upon a successful request.
- Storage Adapters: Built-in support for
localStorage,sessionStorage, andcookies. - TTL Expiry: Automatically cleans up and rotates expired keys based on a configured Time-To-Live.
- Network Adapters: First-class support and helpers for both
fetchandaxios. - TypeScript Ready: Written in TypeScript with full type safety.
Installation
npm install @adetolla/react-idempoor
yarn add @adetolla/react-idempoQuick Start
The simplest way to use @adetolla/react-idempo is with the useIdempotentSubmit hook.
import { useIdempotentSubmit, fetchWithIdempotency } from '@adetolla/react-idempo';
function CheckoutForm() {
const { submit, isPending } = useIdempotentSubmit({
keyName: 'checkout_submit', // unique namespace per form
onSubmit: async (key, formData) => {
// The key is passed as the first argument.
// fetchWithIdempotency automatically attaches it to the 'Idempotency-Key' header.
const response = await fetchWithIdempotency('/api/checkout', {
method: 'POST',
body: JSON.stringify(formData),
idempotencyKey: key,
});
if (!response.ok) throw new Error('Payment failed');
return response.json();
},
onSuccess: (data) => {
alert('Payment successful!');
},
onError: (error) => {
alert('Payment failed, but you can retry safely.');
}
});
return (
<button
onClick={() => submit({ amount: 100, currency: 'USD' })}
disabled={isPending}
>
{isPending ? 'Processing...' : 'Pay Now'}
</button>
);
}Global Configuration (Optional)
You can wrap your application with the IdempotencyProvider to configure global default settings such as the storage mechanism and TTL.
import { IdempotencyProvider, SessionStorageAdapter } from '@adetolla/react-idempo';
function App() {
return (
<IdempotencyProvider
storage={new SessionStorageAdapter()}
ttl={60 * 60 * 1000} // 1 hour TTL
keyPrefix="my_app_idempo_"
>
<CheckoutForm />
</IdempotencyProvider>
);
}Network Adapters
Fetch Adapter
fetchWithIdempotency is a lightweight wrapper around the native fetch API. It automatically adds the Idempotency-Key header if the idempotencyKey option is provided.
import { fetchWithIdempotency } from '@adetolla/react-idempo';
fetchWithIdempotency('/api/data', {
method: 'POST',
idempotencyKey: 'your-uuid-here',
headerName: 'X-Idempotency-Key' // Optional: Custom header name
});Axios Adapter
If you use Axios, you can use the provided interceptor factory.
import axios from 'axios';
import { createAxiosIdempotencyInterceptor } from '@adetolla/react-idempo';
const myAxiosInstance = axios.create();
// A simple example assuming you retrieve the key dynamically
const myKey = "123e4567-e89b-12d3-a456-426614174000";
myAxiosInstance.interceptors.request.use(
createAxiosIdempotencyInterceptor(() => myKey)
);API Reference
useIdempotentSubmit(options)
Options:
keyName(string, optional): The namespace for the storage key. Defaults to'default'.ttl(number, optional): Time-To-Live in milliseconds.onSubmit(function, required): The asynchronous function to execute. Receives theidempotencyKeyas the first argument, followed by any arguments passed to the returnedsubmitfunction.onSuccess(function, optional): Callback executed whenonSubmitresolves successfully.onError(function, optional): Callback executed whenonSubmitthrows an error.generateNewKeyOnSuccess(boolean, optional): Whether to automatically rotate the key on success. Defaults totrue.
Returns:
submit: A function to trigger the submission.isPending: A boolean indicating if the submission is currently in progress.idempotencyKey: The current idempotency key string.
useIdempotencyKey(options)
A lower-level hook if you need direct access to key management without the submit wrapper.
Returns:
idempotencyKey: The current idempotency key string.generateKey: A function to force generation of a new key.clearKey: A function to remove the key from storage.
Storage Adapters
LocalStorageAdapter(default)SessionStorageAdapterCookieStorageAdapter
You can also write your own custom adapter by implementing the StorageAdapter interface:
interface StorageAdapter {
get(key: string): string | null;
set(key: string, value: string, ttl?: number): void;
remove(key: string): void;
}License
ISC
