swr-catalyst 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +457 -0
- package/dist/errors/MutationErrors/index.d.ts +120 -0
- package/dist/errors/MutationErrors/types.d.ts +98 -0
- package/dist/errors/index.d.ts +2 -0
- package/dist/hooks/index.d.ts +4 -0
- package/dist/hooks/shared/helpers/applyOptimisticUpdate/index.d.ts +46 -0
- package/dist/hooks/shared/helpers/createMutationError/index.d.ts +46 -0
- package/dist/hooks/shared/helpers/executeMutation/index.d.ts +51 -0
- package/dist/hooks/shared/helpers/index.d.ts +4 -0
- package/dist/hooks/shared/helpers/rollbackOptimisticUpdate/index.d.ts +31 -0
- package/dist/hooks/shared/index.d.ts +1 -0
- package/dist/hooks/useSWRCreate/index.d.ts +89 -0
- package/dist/hooks/useSWRCreate/index.test.d.ts +1 -0
- package/dist/hooks/useSWRCreate/types.d.ts +27 -0
- package/dist/hooks/useSWRDelete/index.d.ts +103 -0
- package/dist/hooks/useSWRDelete/index.test.d.ts +1 -0
- package/dist/hooks/useSWRDelete/types.d.ts +45 -0
- package/dist/hooks/useSWRUpdate/index.d.ts +121 -0
- package/dist/hooks/useSWRUpdate/index.test.d.ts +1 -0
- package/dist/hooks/useSWRUpdate/types.d.ts +41 -0
- package/dist/hooks/useStableKey/index.d.ts +52 -0
- package/dist/hooks/useStableKey/index.test.d.ts +1 -0
- package/dist/hooks/useStableKey/utils/deepEqual/index.d.ts +11 -0
- package/dist/hooks/useStableKey/utils/index.d.ts +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/swr-catalyst.js +465 -0
- package/dist/swr-catalyst.umd.cjs +1 -0
- package/dist/types/index.d.ts +103 -0
- package/dist/utils/extractSWRKey/index.d.ts +40 -0
- package/dist/utils/extractSWRKey/index.test.d.ts +1 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/mutateByGroup/index.d.ts +51 -0
- package/dist/utils/mutateByGroup/index.test.d.ts +1 -0
- package/dist/utils/mutateById/index.d.ts +50 -0
- package/dist/utils/mutateById/index.test.d.ts +1 -0
- package/dist/utils/resetCache/index.d.ts +48 -0
- package/dist/utils/resetCache/index.test.d.ts +1 -0
- package/dist/utils/swrGetCache/index.d.ts +49 -0
- package/dist/utils/swrGetCache/index.test.d.ts +1 -0
- package/dist/utils/swrMutate/index.d.ts +47 -0
- package/dist/utils/swrMutate/index.test.d.ts +1 -0
- package/dist/utils/to/index.d.ts +49 -0
- package/dist/utils/to/index.test.d.ts +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Cache, ScopedMutator } from 'swr';
|
|
2
|
+
import { SWRKey } from '../../../../types';
|
|
3
|
+
/**
|
|
4
|
+
* Applies an optimistic update to the SWR cache before the mutation completes.
|
|
5
|
+
*
|
|
6
|
+
* This function immediately updates the cache with optimistic data, allowing the UI
|
|
7
|
+
* to reflect changes instantly before the server responds. It saves the original data
|
|
8
|
+
* so it can be rolled back if the mutation fails.
|
|
9
|
+
*
|
|
10
|
+
* @template TData - The type of data being mutated
|
|
11
|
+
* @template TCache - The type of the cached data
|
|
12
|
+
*
|
|
13
|
+
* @param cache - The SWR cache instance from useSWRConfig()
|
|
14
|
+
* @param mutate - The SWR mutate function from useSWRConfig()
|
|
15
|
+
* @param stableKey - The stable SWR cache key to update
|
|
16
|
+
* @param payload - Configuration object containing:
|
|
17
|
+
* - `data`: The new data being sent to the server
|
|
18
|
+
* - `optimisticUpdateFn`: Function that receives current cache data and new data,
|
|
19
|
+
* and returns the optimistically updated cache
|
|
20
|
+
*
|
|
21
|
+
* @returns The original cache data before the optimistic update, for potential rollback
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* // Add new todo optimistically
|
|
25
|
+
* const originalData = await applyOptimisticUpdate(cache, mutate, key, {
|
|
26
|
+
* data: newTodo,
|
|
27
|
+
* optimisticUpdateFn: (todos, newTodo) => [...todos, { ...newTodo, id: 'temp' }]
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* // Update todo optimistically
|
|
32
|
+
* const originalData = await applyOptimisticUpdate(cache, mutate, key, {
|
|
33
|
+
* data: { id: 123, title: 'Updated' },
|
|
34
|
+
* optimisticUpdateFn: (todos, update) =>
|
|
35
|
+
* todos.map(t => t.id === update.id ? { ...t, ...update } : t)
|
|
36
|
+
* });
|
|
37
|
+
*
|
|
38
|
+
* @remarks
|
|
39
|
+
* - This function is used internally by mutation hooks when `optimisticUpdate` option is provided
|
|
40
|
+
* - The mutation is applied without revalidation (false flag) to prevent immediate server fetch
|
|
41
|
+
* - Always save the return value to enable rollback on error
|
|
42
|
+
*/
|
|
43
|
+
export declare function applyOptimisticUpdate<TData, TCache>(cache: Cache, mutate: ScopedMutator, stableKey: SWRKey, payload: {
|
|
44
|
+
data: TData;
|
|
45
|
+
optimisticUpdateFn: (currentData: TCache | undefined, newData: TData) => TCache;
|
|
46
|
+
}): Promise<TCache | undefined>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { MutationError } from '../../../../errors';
|
|
2
|
+
import { SWRKey } from '../../../../types';
|
|
3
|
+
/**
|
|
4
|
+
* Creates a standardized MutationError with contextual information.
|
|
5
|
+
*
|
|
6
|
+
* This helper function constructs a consistent error object across all mutation hooks,
|
|
7
|
+
* including the operation type, resource information, and original error. It generates
|
|
8
|
+
* a descriptive error message that includes the operation, resource name, and any ID.
|
|
9
|
+
*
|
|
10
|
+
* @param operation - The type of mutation operation: 'create', 'update', or 'delete'
|
|
11
|
+
* @param stableKey - The SWR cache key that was being mutated (or null if unknown)
|
|
12
|
+
* @param err - The original error thrown by the mutation function
|
|
13
|
+
* @param additionalContext - Optional additional context:
|
|
14
|
+
* - `data`: The data being sent (for create/update operations)
|
|
15
|
+
* - `id`: The resource ID (for update/delete operations)
|
|
16
|
+
*
|
|
17
|
+
* @returns A new MutationError instance with full context
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* // Create error for failed creation
|
|
21
|
+
* const error = createMutationError('create', key, err, {
|
|
22
|
+
* data: { title: 'New todo' }
|
|
23
|
+
* });
|
|
24
|
+
* // Error message: "Failed to create resource "todos". Network error"
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* // Create error for failed update with ID
|
|
28
|
+
* const error = createMutationError('update', key, err, {
|
|
29
|
+
* data: { title: 'Updated' },
|
|
30
|
+
* id: 123
|
|
31
|
+
* });
|
|
32
|
+
* // Error message: "Failed to update resource "todos" with ID 123. Validation error"
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // Create error for failed deletion
|
|
36
|
+
* const error = createMutationError('delete', key, err, { id: 123 });
|
|
37
|
+
* // Error message: "Failed to delete resource "todos" with ID 123. Not found"
|
|
38
|
+
*
|
|
39
|
+
* @remarks
|
|
40
|
+
* This function is used internally by all mutation hooks to create consistent error objects.
|
|
41
|
+
* The resulting MutationError includes helper methods like `isNetworkError()` and `getUserMessage()`.
|
|
42
|
+
*/
|
|
43
|
+
export declare function createMutationError(operation: "create" | "update" | "delete", stableKey: SWRKey, err: unknown, additionalContext?: {
|
|
44
|
+
data?: unknown;
|
|
45
|
+
id?: string | number;
|
|
46
|
+
}): MutationError;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { ScopedMutator } from 'swr';
|
|
2
|
+
import { MutationError } from '../../../../errors';
|
|
3
|
+
import { SWRKey } from '../../../../types';
|
|
4
|
+
/**
|
|
5
|
+
* Executes a mutation function with automatic error handling and cache rollback.
|
|
6
|
+
*
|
|
7
|
+
* This helper function orchestrates the mutation execution flow:
|
|
8
|
+
* 1. Executes the mutation function
|
|
9
|
+
* 2. On success: Revalidates the cache
|
|
10
|
+
* 3. On error: Optionally rolls back optimistic updates, calls error handler, and re-throws
|
|
11
|
+
*
|
|
12
|
+
* @template TResult - The type of data returned by the mutation
|
|
13
|
+
* @template TCache - The type of the cached data
|
|
14
|
+
*
|
|
15
|
+
* @param mutationFn - The async function to execute (API call)
|
|
16
|
+
* @param options - Configuration object:
|
|
17
|
+
* - `mutate`: The SWR mutate function from useSWRConfig()
|
|
18
|
+
* - `stableKey`: The stable SWR cache key
|
|
19
|
+
* - `shouldRollback`: Whether to rollback optimistic update on error
|
|
20
|
+
* - `originalData`: The original cache data to restore on rollback
|
|
21
|
+
* - `onError`: Callback function to handle errors (typically sets error state)
|
|
22
|
+
*
|
|
23
|
+
* @returns The result from the mutation function
|
|
24
|
+
* @throws Re-throws the MutationError after handling rollback and error callback
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* // Used internally by mutation hooks
|
|
28
|
+
* const result = await executeMutation(
|
|
29
|
+
* () => api.post('/todos', newTodo),
|
|
30
|
+
* {
|
|
31
|
+
* mutate,
|
|
32
|
+
* stableKey,
|
|
33
|
+
* shouldRollback: rollbackOnError && !!optimisticUpdate,
|
|
34
|
+
* originalData,
|
|
35
|
+
* onError: (err) => setError(err)
|
|
36
|
+
* }
|
|
37
|
+
* );
|
|
38
|
+
*
|
|
39
|
+
* @remarks
|
|
40
|
+
* - This function is used internally by mutation hooks to reduce code duplication
|
|
41
|
+
* - Automatically revalidates cache on success
|
|
42
|
+
* - Handles rollback only if both shouldRollback is true and originalData exists
|
|
43
|
+
* - Always calls onError callback before re-throwing to allow state updates
|
|
44
|
+
*/
|
|
45
|
+
export declare function executeMutation<TResult, TCache>(mutationFn: () => Promise<TResult>, options: {
|
|
46
|
+
mutate: ScopedMutator;
|
|
47
|
+
stableKey: SWRKey;
|
|
48
|
+
shouldRollback: boolean;
|
|
49
|
+
originalData?: TCache;
|
|
50
|
+
onError: (error: MutationError) => void;
|
|
51
|
+
}): Promise<TResult>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ScopedMutator } from 'swr';
|
|
2
|
+
import { SWRKey } from '../../../../types';
|
|
3
|
+
/**
|
|
4
|
+
* Rolls back an optimistic update by restoring the original cache data.
|
|
5
|
+
*
|
|
6
|
+
* This function is called when a mutation fails and `rollbackOnError` is true.
|
|
7
|
+
* It restores the cache to its state before the optimistic update was applied,
|
|
8
|
+
* ensuring the UI accurately reflects the server state.
|
|
9
|
+
*
|
|
10
|
+
* @param mutate - The SWR mutate function from useSWRConfig()
|
|
11
|
+
* @param stableKey - The stable SWR cache key to restore
|
|
12
|
+
* @param originalData - The original cache data to restore (saved from applyOptimisticUpdate)
|
|
13
|
+
*
|
|
14
|
+
* @returns Promise that resolves when the rollback is complete
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* // Used internally by mutation hooks on error
|
|
18
|
+
* try {
|
|
19
|
+
* const result = await apiCall();
|
|
20
|
+
* } catch (error) {
|
|
21
|
+
* // Restore original data if mutation fails
|
|
22
|
+
* await rollbackOptimisticUpdate(mutate, key, originalData);
|
|
23
|
+
* throw error;
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* @remarks
|
|
27
|
+
* - This function is automatically called by mutation hooks when `rollbackOnError: true` (default)
|
|
28
|
+
* - The mutation is applied without revalidation (false flag) for immediate UI update
|
|
29
|
+
* - Set `rollbackOnError: false` in mutation options to keep optimistic data on error
|
|
30
|
+
*/
|
|
31
|
+
export declare function rollbackOptimisticUpdate(mutate: ScopedMutator, stableKey: SWRKey, originalData: unknown): Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './helpers';
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { MutateOptions, SWRKey } from '../../types';
|
|
2
|
+
import { CreateFunction } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* A hook for creating new data with SWR cache management and optimistic updates.
|
|
5
|
+
*
|
|
6
|
+
* @template TData - The type of data being created (e.g., `{ title: string }` for a new todo)
|
|
7
|
+
* @template TCache - The type of the cached data (e.g., `Todo[]` for a list of todos)
|
|
8
|
+
* @template TError - The type of error that might be thrown (defaults to `Error`)
|
|
9
|
+
*
|
|
10
|
+
* @param key - The structured SWR cache key to revalidate after creation.
|
|
11
|
+
* Must be an object with `id`, optional `group`, and `data` properties, or `null`.
|
|
12
|
+
* **Important:** The `key` object should have a stable reference (use `useMemo` if creating inline).
|
|
13
|
+
* The `data` property should be a primitive value (string, number) or a stable reference.
|
|
14
|
+
* Example: `{ id: 'todos', group: 'lists', data: '/api/todos' }`
|
|
15
|
+
*
|
|
16
|
+
* @param createFunction - The async function that performs the API call.
|
|
17
|
+
* Should accept the new data and return the created item from the server.
|
|
18
|
+
* Example: `async (newTodo) => api.post('/todos', newTodo)`
|
|
19
|
+
*
|
|
20
|
+
* @param options - Optional configuration:
|
|
21
|
+
* - `optimisticUpdate`: Function to update cache immediately before API call completes.
|
|
22
|
+
* Receives `(currentData, newData)` and should return the optimistically updated cache.
|
|
23
|
+
* Example: `(todos, newTodo) => [...todos, { ...newTodo, id: 'temp-id' }]`
|
|
24
|
+
* - `rollbackOnError`: Whether to revert optimistic update if API fails (default: `true`)
|
|
25
|
+
*
|
|
26
|
+
* @returns An object containing:
|
|
27
|
+
* - `trigger`: Async function to call when creating data. Returns the created data from server.
|
|
28
|
+
* - `isMutating`: Boolean indicating if mutation is in progress (useful for loading states)
|
|
29
|
+
* - `error`: Error object if mutation failed, otherwise `null`
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* // Basic usage with structured key
|
|
33
|
+
* const { trigger, isMutating, error } = useSWRCreate(
|
|
34
|
+
* { id: 'todos', group: 'lists', data: '/api/todos' },
|
|
35
|
+
* async (newTodo) => api.post('/todos', newTodo)
|
|
36
|
+
* );
|
|
37
|
+
*
|
|
38
|
+
* await trigger({ title: 'Buy milk' });
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* // With optimistic updates
|
|
42
|
+
* const { trigger, isMutating } = useSWRCreate(
|
|
43
|
+
* { id: 'todos', data: '/api/todos' },
|
|
44
|
+
* createTodoAPI,
|
|
45
|
+
* {
|
|
46
|
+
* optimisticUpdate: (todos, newTodo) => [
|
|
47
|
+
* ...todos,
|
|
48
|
+
* { ...newTodo, id: 'temp-' + Date.now() }
|
|
49
|
+
* ],
|
|
50
|
+
* rollbackOnError: true
|
|
51
|
+
* }
|
|
52
|
+
* );
|
|
53
|
+
*
|
|
54
|
+
* // UI updates immediately, then syncs with server
|
|
55
|
+
* await trigger({ title: 'New task', completed: false });
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* // With error handling
|
|
59
|
+
* const { trigger, error } = useSWRCreate(
|
|
60
|
+
* { id: 'todos', data: '/api/todos' },
|
|
61
|
+
* createTodoAPI
|
|
62
|
+
* );
|
|
63
|
+
*
|
|
64
|
+
* try {
|
|
65
|
+
* const created = await trigger({ title: 'New todo' });
|
|
66
|
+
* console.log('Created with ID:', created.id);
|
|
67
|
+
* } catch (err) {
|
|
68
|
+
* console.error('Failed to create:', err);
|
|
69
|
+
* }
|
|
70
|
+
*
|
|
71
|
+
* // Or use the error state
|
|
72
|
+
* if (error) {
|
|
73
|
+
* return <div>Error: {error.message}</div>;
|
|
74
|
+
* }
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* // Using with group for batch cache operations
|
|
78
|
+
* const { trigger } = useSWRCreate(
|
|
79
|
+
* { id: 'user-todos', group: 'user-data', data: '/api/user/todos' },
|
|
80
|
+
* createTodoAPI
|
|
81
|
+
* );
|
|
82
|
+
*
|
|
83
|
+
* // Later, you can use mutateByGroup('user-data') to revalidate all related caches
|
|
84
|
+
*/
|
|
85
|
+
export declare function useSWRCreate<TData = unknown, TCache = unknown, TError = Error>(key: SWRKey<TData>, createFunction: CreateFunction<TData, TCache>, options?: MutateOptions<TCache, TData>): {
|
|
86
|
+
trigger: (data: TData) => Promise<TCache | undefined>;
|
|
87
|
+
isMutating: boolean;
|
|
88
|
+
error: TError | null;
|
|
89
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Function type for creating a new resource via API.
|
|
3
|
+
*
|
|
4
|
+
* @template TData - The type of data being sent to create the resource
|
|
5
|
+
* @template TResult - The type of data returned from the API
|
|
6
|
+
*
|
|
7
|
+
* @param data - The data for the new resource
|
|
8
|
+
* @returns Promise resolving to the created resource from the server
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* // Basic create function
|
|
12
|
+
* const createTodo: CreateFunction<TodoInput, Todo> = async (data) => {
|
|
13
|
+
* const response = await fetch('/api/todos', {
|
|
14
|
+
* method: 'POST',
|
|
15
|
+
* body: JSON.stringify(data),
|
|
16
|
+
* });
|
|
17
|
+
* return response.json();
|
|
18
|
+
* };
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* // With axios
|
|
22
|
+
* const createUser: CreateFunction<UserInput, User> = async (data) => {
|
|
23
|
+
* const { data: user } = await axios.post('/api/users', data);
|
|
24
|
+
* return user;
|
|
25
|
+
* };
|
|
26
|
+
*/
|
|
27
|
+
export type CreateFunction<TData, TResult> = (data: TData) => Promise<TResult>;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { MutateOptions, SWRKey } from '../../types';
|
|
2
|
+
import { DeleteFunction } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* A hook for deleting data with SWR cache management and optimistic updates.
|
|
5
|
+
*
|
|
6
|
+
* @template TCache - The type of the cached data (e.g., `Todo[]` for a list of todos)
|
|
7
|
+
* @template TResult - The type of the result returned by the delete API (e.g., `{ success: boolean }`)
|
|
8
|
+
* @template TError - The type of error that might be thrown (defaults to `Error`)
|
|
9
|
+
*
|
|
10
|
+
* @param key - The structured SWR cache key to revalidate after deletion.
|
|
11
|
+
* Must be an object with `id`, optional `group`, and `data` properties, or `null`.
|
|
12
|
+
* **Important:** The `key` object should have a stable reference (use `useMemo` if creating inline).
|
|
13
|
+
* The `data` property should be a primitive value (string, number) or a stable reference.
|
|
14
|
+
* Example: `{ id: 'todos', group: 'lists', data: '/api/todos' }`
|
|
15
|
+
*
|
|
16
|
+
* @param deleteFunction - The async function that performs the API delete call.
|
|
17
|
+
* Should accept an ID and return the result from the server.
|
|
18
|
+
* Example: `async (todoId) => api.delete(\`/todos/\${todoId}\`)`
|
|
19
|
+
*
|
|
20
|
+
* @param options - Optional configuration:
|
|
21
|
+
* - `optimisticUpdate`: Function to update cache immediately before API call completes.
|
|
22
|
+
* Receives `(currentData, itemId)` and should return the optimistically updated cache.
|
|
23
|
+
* Example: `(todos, id) => todos.filter(todo => todo.id !== id)`
|
|
24
|
+
* - `rollbackOnError`: Whether to revert optimistic update if API fails (default: `true`)
|
|
25
|
+
*
|
|
26
|
+
* @returns An object containing:
|
|
27
|
+
* - `trigger`: Async function to call when deleting data. Accepts the ID and returns the API result.
|
|
28
|
+
* - `isMutating`: Boolean indicating if mutation is in progress (useful for loading states)
|
|
29
|
+
* - `error`: Error object if mutation failed, otherwise `null`
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* // Basic usage with structured key
|
|
33
|
+
* const { trigger, isMutating, error } = useSWRDelete(
|
|
34
|
+
* { id: 'todos', group: 'lists', data: '/api/todos' },
|
|
35
|
+
* async (todoId) => api.delete(\`/todos/\${todoId}\`)
|
|
36
|
+
* );
|
|
37
|
+
*
|
|
38
|
+
* await trigger(123);
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* // With optimistic updates (instant removal from UI)
|
|
42
|
+
* const { trigger, isMutating } = useSWRDelete(
|
|
43
|
+
* { id: 'todos', data: '/api/todos' },
|
|
44
|
+
* deleteTodoAPI,
|
|
45
|
+
* {
|
|
46
|
+
* optimisticUpdate: (todos, todoId) =>
|
|
47
|
+
* todos.filter(todo => todo.id !== todoId),
|
|
48
|
+
* rollbackOnError: true
|
|
49
|
+
* }
|
|
50
|
+
* );
|
|
51
|
+
*
|
|
52
|
+
* // Todo disappears immediately, then syncs with server
|
|
53
|
+
* await trigger(todoId);
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* // With error handling
|
|
57
|
+
* const { trigger, error } = useSWRDelete(
|
|
58
|
+
* { id: 'todos', data: '/api/todos' },
|
|
59
|
+
* deleteTodoAPI
|
|
60
|
+
* );
|
|
61
|
+
*
|
|
62
|
+
* try {
|
|
63
|
+
* const result = await trigger(todoId);
|
|
64
|
+
* console.log('Deleted:', result);
|
|
65
|
+
* } catch (err) {
|
|
66
|
+
* console.error('Failed to delete:', err);
|
|
67
|
+
* }
|
|
68
|
+
*
|
|
69
|
+
* // Or use the error state
|
|
70
|
+
* if (error) {
|
|
71
|
+
* return <div>Error: {error.message}</div>;
|
|
72
|
+
* }
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* // With loading state for button
|
|
76
|
+
* const { trigger, isMutating } = useSWRDelete(
|
|
77
|
+
* { id: 'todos', data: '/api/todos' },
|
|
78
|
+
* deleteTodoAPI
|
|
79
|
+
* );
|
|
80
|
+
*
|
|
81
|
+
* return (
|
|
82
|
+
* <button
|
|
83
|
+
* onClick={() => trigger(todoId)}
|
|
84
|
+
* disabled={isMutating}
|
|
85
|
+
* >
|
|
86
|
+
* {isMutating ? 'Deleting...' : 'Delete'}
|
|
87
|
+
* </button>
|
|
88
|
+
* );
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* // Using with group for batch cache operations
|
|
92
|
+
* const { trigger } = useSWRDelete(
|
|
93
|
+
* { id: 'user-todos', group: 'user-data', data: '/api/user/todos' },
|
|
94
|
+
* deleteTodoAPI
|
|
95
|
+
* );
|
|
96
|
+
*
|
|
97
|
+
* // Later, you can use mutateByGroup('user-data') to revalidate all related caches
|
|
98
|
+
*/
|
|
99
|
+
export declare function useSWRDelete<TCache = unknown, TResult = unknown, TError = Error>(key: SWRKey, deleteFunction: DeleteFunction<TResult>, options?: MutateOptions<TCache, string | number>): {
|
|
100
|
+
trigger: (id: string | number) => Promise<TResult | undefined>;
|
|
101
|
+
isMutating: boolean;
|
|
102
|
+
error: TError | null;
|
|
103
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Function type for deleting a resource via API.
|
|
3
|
+
*
|
|
4
|
+
* @template TResult - The type of data returned from the API (e.g., confirmation message, deleted item)
|
|
5
|
+
*
|
|
6
|
+
* @param id - The unique identifier of the resource to delete
|
|
7
|
+
* @returns Promise resolving to the API response (often a success message or the deleted item)
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* // Basic delete function
|
|
11
|
+
* const deleteTodo: DeleteFunction<{ success: boolean }> = async (id) => {
|
|
12
|
+
* const response = await fetch(`/api/todos/${id}`, {
|
|
13
|
+
* method: 'DELETE',
|
|
14
|
+
* });
|
|
15
|
+
* return response.json();
|
|
16
|
+
* };
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* // With axios, returning deleted item
|
|
20
|
+
* const deleteUser: DeleteFunction<User> = async (id) => {
|
|
21
|
+
* const { data } = await axios.delete(`/api/users/${id}`);
|
|
22
|
+
* return data;
|
|
23
|
+
* };
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* // With error handling
|
|
27
|
+
* const deletePost: DeleteFunction<void> = async (id) => {
|
|
28
|
+
* const response = await fetch(`/api/posts/${id}`, {
|
|
29
|
+
* method: 'DELETE',
|
|
30
|
+
* });
|
|
31
|
+
* if (!response.ok) {
|
|
32
|
+
* throw new Error(`Failed to delete: ${response.statusText}`);
|
|
33
|
+
* }
|
|
34
|
+
* };
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* // Returning just status code
|
|
38
|
+
* const deleteComment: DeleteFunction<number> = async (id) => {
|
|
39
|
+
* const response = await fetch(`/api/comments/${id}`, {
|
|
40
|
+
* method: 'DELETE',
|
|
41
|
+
* });
|
|
42
|
+
* return response.status;
|
|
43
|
+
* };
|
|
44
|
+
*/
|
|
45
|
+
export type DeleteFunction<TResult> = (id: string | number) => Promise<TResult>;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { MutateOptions, SWRKey } from '../../types';
|
|
2
|
+
import { UpdateFunction } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* A hook for updating existing data with SWR cache management and optimistic updates.
|
|
5
|
+
*
|
|
6
|
+
* @template TData - The type of data being updated (e.g., `{ title: string }` for updating a todo)
|
|
7
|
+
* @template TCache - The type of the cached data (e.g., `Todo[]` for a list of todos)
|
|
8
|
+
* @template TError - The type of error that might be thrown (defaults to `Error`)
|
|
9
|
+
*
|
|
10
|
+
* @param key - The structured SWR cache key to revalidate after update.
|
|
11
|
+
* Must be an object with `id`, optional `group`, and `data` properties, or `null`.
|
|
12
|
+
* **Important:** The `key` object should have a stable reference (use `useMemo` if creating inline).
|
|
13
|
+
* The `data` property should be a primitive value (string, number) or a stable reference.
|
|
14
|
+
* Example: `{ id: 'todos', group: 'lists', data: '/api/todos' }`
|
|
15
|
+
*
|
|
16
|
+
* @param updateFunction - The async function that performs the API update call.
|
|
17
|
+
* Should accept an ID and the update data, returning the updated item from the server.
|
|
18
|
+
* Example: `async (todoId, updates) => api.patch(\`/todos/\${todoId}\`, updates)`
|
|
19
|
+
*
|
|
20
|
+
* @param options - Optional configuration:
|
|
21
|
+
* - `optimisticUpdate`: Function to update cache immediately before API call completes.
|
|
22
|
+
* Receives `(currentData, { id, data })` and should return the optimistically updated cache.
|
|
23
|
+
* Example: `(todos, { id, data }) => todos.map(t => t.id === id ? { ...t, ...data } : t)`
|
|
24
|
+
* - `rollbackOnError`: Whether to revert optimistic update if API fails (default: `true`)
|
|
25
|
+
*
|
|
26
|
+
* @returns An object containing:
|
|
27
|
+
* - `trigger`: Async function to call when updating data. Accepts ID and data, returns updated item.
|
|
28
|
+
* - `isMutating`: Boolean indicating if mutation is in progress (useful for loading states)
|
|
29
|
+
* - `error`: Error object if mutation failed, otherwise `null`
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* // Basic usage with structured key
|
|
33
|
+
* const { trigger, isMutating, error } = useSWRUpdate(
|
|
34
|
+
* { id: 'todos', group: 'lists', data: '/api/todos' },
|
|
35
|
+
* async (todoId, updates) => api.patch(\`/todos/\${todoId}\`, updates)
|
|
36
|
+
* );
|
|
37
|
+
*
|
|
38
|
+
* await trigger(123, { completed: true });
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* // With optimistic updates (instant UI update)
|
|
42
|
+
* const { trigger, isMutating } = useSWRUpdate(
|
|
43
|
+
* { id: 'todos', data: '/api/todos' },
|
|
44
|
+
* updateTodoAPI,
|
|
45
|
+
* {
|
|
46
|
+
* optimisticUpdate: (todos, { id, data }) =>
|
|
47
|
+
* todos.map(todo =>
|
|
48
|
+
* todo.id === id ? { ...todo, ...data } : todo
|
|
49
|
+
* ),
|
|
50
|
+
* rollbackOnError: true
|
|
51
|
+
* }
|
|
52
|
+
* );
|
|
53
|
+
*
|
|
54
|
+
* // Todo updates immediately, then syncs with server
|
|
55
|
+
* await trigger(todoId, { title: 'Updated title' });
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* // With error handling
|
|
59
|
+
* const { trigger, error } = useSWRUpdate(
|
|
60
|
+
* { id: 'todos', data: '/api/todos' },
|
|
61
|
+
* updateTodoAPI
|
|
62
|
+
* );
|
|
63
|
+
*
|
|
64
|
+
* try {
|
|
65
|
+
* const updated = await trigger(todoId, { completed: true });
|
|
66
|
+
* console.log('Updated:', updated);
|
|
67
|
+
* } catch (err) {
|
|
68
|
+
* console.error('Failed to update:', err);
|
|
69
|
+
* }
|
|
70
|
+
*
|
|
71
|
+
* // Or use the error state
|
|
72
|
+
* if (error) {
|
|
73
|
+
* return <div>Error: {error.message}</div>;
|
|
74
|
+
* }
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* // With loading state for inline editing
|
|
78
|
+
* const { trigger, isMutating } = useSWRUpdate(
|
|
79
|
+
* { id: 'todos', data: '/api/todos' },
|
|
80
|
+
* updateTodoAPI
|
|
81
|
+
* );
|
|
82
|
+
*
|
|
83
|
+
* return (
|
|
84
|
+
* <input
|
|
85
|
+
* onBlur={(e) => trigger(todoId, { title: e.target.value })}
|
|
86
|
+
* disabled={isMutating}
|
|
87
|
+
* />
|
|
88
|
+
* );
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* // Toggle boolean field
|
|
92
|
+
* const { trigger } = useSWRUpdate(
|
|
93
|
+
* { id: 'todos', data: '/api/todos' },
|
|
94
|
+
* updateTodoAPI,
|
|
95
|
+
* {
|
|
96
|
+
* optimisticUpdate: (todos, { id, data }) =>
|
|
97
|
+
* todos.map(t => t.id === id ? { ...t, ...data } : t)
|
|
98
|
+
* }
|
|
99
|
+
* );
|
|
100
|
+
*
|
|
101
|
+
* const toggleComplete = (todo) => {
|
|
102
|
+
* trigger(todo.id, { completed: !todo.completed });
|
|
103
|
+
* };
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* // Using with group for batch cache operations
|
|
107
|
+
* const { trigger } = useSWRUpdate(
|
|
108
|
+
* { id: 'user-todos', group: 'user-data', data: '/api/user/todos' },
|
|
109
|
+
* updateTodoAPI
|
|
110
|
+
* );
|
|
111
|
+
*
|
|
112
|
+
* // Later, you can use mutateByGroup('user-data') to revalidate all related caches
|
|
113
|
+
*/
|
|
114
|
+
export declare function useSWRUpdate<TData = unknown, TCache = unknown, TError = Error>(key: SWRKey, updateFunction: UpdateFunction<TData, TCache>, options?: MutateOptions<TCache, {
|
|
115
|
+
id: string | number;
|
|
116
|
+
data: TData;
|
|
117
|
+
}>): {
|
|
118
|
+
trigger: (id: string | number, data: TData) => Promise<TCache | undefined>;
|
|
119
|
+
isMutating: boolean;
|
|
120
|
+
error: TError | null;
|
|
121
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Function type for updating an existing resource via API.
|
|
3
|
+
*
|
|
4
|
+
* @template TData - The type of data being sent to update the resource
|
|
5
|
+
* @template TResult - The type of data returned from the API
|
|
6
|
+
*
|
|
7
|
+
* @param id - The unique identifier of the resource to update
|
|
8
|
+
* @param data - The partial or complete data to update the resource with
|
|
9
|
+
* @returns Promise resolving to the updated resource from the server
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* // Basic update function with PATCH
|
|
13
|
+
* const updateTodo: UpdateFunction<TodoUpdate, Todo> = async (id, data) => {
|
|
14
|
+
* const response = await fetch(`/api/todos/${id}`, {
|
|
15
|
+
* method: 'PATCH',
|
|
16
|
+
* body: JSON.stringify(data),
|
|
17
|
+
* });
|
|
18
|
+
* return response.json();
|
|
19
|
+
* };
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* // With axios and PUT
|
|
23
|
+
* const updateUser: UpdateFunction<UserUpdate, User> = async (id, data) => {
|
|
24
|
+
* const { data: user } = await axios.put(`/api/users/${id}`, data);
|
|
25
|
+
* return user;
|
|
26
|
+
* };
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* // With error handling
|
|
30
|
+
* const updatePost: UpdateFunction<PostUpdate, Post> = async (id, data) => {
|
|
31
|
+
* const response = await fetch(`/api/posts/${id}`, {
|
|
32
|
+
* method: 'PATCH',
|
|
33
|
+
* body: JSON.stringify(data),
|
|
34
|
+
* });
|
|
35
|
+
* if (!response.ok) {
|
|
36
|
+
* throw new Error(`Failed to update post: ${response.statusText}`);
|
|
37
|
+
* }
|
|
38
|
+
* return response.json();
|
|
39
|
+
* };
|
|
40
|
+
*/
|
|
41
|
+
export type UpdateFunction<TData, TResult> = (id: string | number, data: TData) => Promise<TResult>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { SWRKey } from '../../types';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a stable reference for SWR keys to prevent unnecessary re-renders.
|
|
4
|
+
*
|
|
5
|
+
* This hook memoizes the SWR key based on its individual properties (id, group, data)
|
|
6
|
+
* using deep equality comparison. This ensures that the key object remains stable
|
|
7
|
+
* across renders when its properties haven't changed by value, not just by reference.
|
|
8
|
+
*
|
|
9
|
+
* **Key Feature:** Handles object data with deep comparison, so you don't need to
|
|
10
|
+
* wrap complex data in `useMemo` yourself. The hook automatically detects when the
|
|
11
|
+
* actual values change vs when only the object reference changes.
|
|
12
|
+
*
|
|
13
|
+
* @template T - The type of the data property in the SWR key
|
|
14
|
+
*
|
|
15
|
+
* @param key - The SWR key object to stabilize, or null if not ready
|
|
16
|
+
*
|
|
17
|
+
* @returns A memoized SWR key with stable reference, or null if key is null or invalid
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* // Works with primitive data (string URLs)
|
|
21
|
+
* const { trigger } = useSWRCreate(
|
|
22
|
+
* { id: 'todos', data: '/api/todos' },
|
|
23
|
+
* createTodo
|
|
24
|
+
* );
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* // Also works with object data - no useMemo needed!
|
|
28
|
+
* function TodoList({ userId }: { userId: number }) {
|
|
29
|
+
* const { trigger } = useSWRCreate(
|
|
30
|
+
* // This is now safe! Hook handles object comparison
|
|
31
|
+
* { id: 'todos', data: { url: '/api/todos', userId } },
|
|
32
|
+
* createTodo
|
|
33
|
+
* );
|
|
34
|
+
* }
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* // Stable reference even with dynamic query params
|
|
38
|
+
* function FilteredList({ filter }: { filter: string }) {
|
|
39
|
+
* const { trigger } = useSWRCreate(
|
|
40
|
+
* // Hook detects when filter value changes, not just object reference
|
|
41
|
+
* { id: 'todos', data: { url: '/api/todos', params: { filter } } },
|
|
42
|
+
* createTodo
|
|
43
|
+
* );
|
|
44
|
+
* }
|
|
45
|
+
*
|
|
46
|
+
* @remarks
|
|
47
|
+
* - Uses deep equality comparison via JSON serialization for object data
|
|
48
|
+
* - Falls back to reference equality for non-serializable values
|
|
49
|
+
* - Small performance cost (~1ms) for deep comparison on each render
|
|
50
|
+
* - This hook is used internally by all mutation hooks (useSWRCreate, useSWRUpdate, useSWRDelete)
|
|
51
|
+
*/
|
|
52
|
+
export declare function useStableKey<T = unknown>(key: SWRKey<T> | null): SWRKey<T> | null;
|