diff --git a/src/hooks/useBulkUpdateVariables.ts b/src/hooks/useBulkUpdateVariables.ts index 7ff7181..82fe3a8 100644 --- a/src/hooks/useBulkUpdateVariables.ts +++ b/src/hooks/useBulkUpdateVariables.ts @@ -15,21 +15,35 @@ import { mutator } from 'api/mutator' * This hook is designed to perform a batch operation for creating, updating, and deleting variables, collections, and modes. * It provides an ergonomic API with `mutate` and loading/error state for easy integration. * + * ## Return Value + * + * The `mutate` function returns `Promise`: + * - On success: Returns the API response data + * - On error: Returns `undefined` (error stored in `error` state) + * + * Use `isSuccess`/`isError` flags or check the return value to handle results. + * + * @returns MutationResult with `mutate`, status flags (`isLoading`, `isSuccess`, `isError`), + * `data` (API response), and `error` (if failed). + * * @example * ```tsx * import { useBulkUpdateVariables } from '@figma-vars/hooks'; * * function BulkUpdateButton() { - * const { mutate, isLoading, error } = useBulkUpdateVariables(); + * const { mutate, isLoading, isError, error } = useBulkUpdateVariables(); * - * const handleBulkUpdate = () => { - * mutate({ + * const handleBulkUpdate = async () => { + * const result = await mutate({ * variables: [{ action: 'UPDATE', id: 'VariableId:123', name: 'new-name' }], * }); + * if (result) { + * console.log('Bulk update successful'); + * } * }; * * if (isLoading) return
Updating...
; - * if (error) return
Error: {error.message}
; + * if (isError) return
Error: {error?.message}
; * return ; * } * ``` diff --git a/src/hooks/useCreateVariable.ts b/src/hooks/useCreateVariable.ts index cf0063c..d7907fa 100644 --- a/src/hooks/useCreateVariable.ts +++ b/src/hooks/useCreateVariable.ts @@ -14,19 +14,37 @@ import { mutator } from 'api/mutator' * @remarks * The hook returns a `mutate` function to trigger the creation along with state flags and data. * + * ## Return Value + * + * The `mutate` function returns `Promise`: + * - On success: Returns the API response data + * - On error: Returns `undefined` (error stored in `error` state) + * + * Use `isSuccess`/`isError` flags or check the return value to handle results. + * + * @returns MutationResult with `mutate`, status flags (`isLoading`, `isSuccess`, `isError`), + * `data` (API response), and `error` (if failed). + * * @example * ```tsx * import { useCreateVariable } from '@figma-vars/hooks'; * * function CreateVariableButton() { - * const { mutate, isLoading, error } = useCreateVariable(); + * const { mutate, isLoading, isError, error } = useCreateVariable(); * - * const handleCreate = () => { - * mutate({ name: 'new-variable', variableCollectionId: 'VariableCollectionId:1:1', resolvedType: 'COLOR' }); + * const handleCreate = async () => { + * const result = await mutate({ + * name: 'new-variable', + * variableCollectionId: 'VariableCollectionId:1:1', + * resolvedType: 'COLOR' + * }); + * if (result) { + * console.log('Created successfully:', result); + * } * }; * * if (isLoading) return
Creating...
; - * if (error) return
Error: {error.message}
; + * if (isError) return
Error: {error?.message}
; * return ; * } * ``` diff --git a/src/hooks/useDeleteVariable.ts b/src/hooks/useDeleteVariable.ts index 7db88d2..e0d4f66 100644 --- a/src/hooks/useDeleteVariable.ts +++ b/src/hooks/useDeleteVariable.ts @@ -13,17 +13,33 @@ import { mutator } from 'api/mutator' * @remarks * This hook provides a `mutate` function to trigger the deletion and exposes loading and error states. * + * ## Return Value + * + * The `mutate` function returns `Promise`: + * - On success: Returns the API response data + * - On error: Returns `undefined` (error stored in `error` state) + * + * Use `isSuccess`/`isError` flags or check the return value to handle results. + * + * @returns MutationResult with `mutate`, status flags (`isLoading`, `isSuccess`, `isError`), + * `data` (API response), and `error` (if failed). + * * @example * ```tsx * import { useDeleteVariable } from '@figma-vars/hooks'; * * function DeleteVariableButton({ id }: { id: string }) { - * const { mutate, isLoading, error } = useDeleteVariable(); + * const { mutate, isLoading, isError, error } = useDeleteVariable(); * - * const onDelete = () => mutate(id); + * const onDelete = async () => { + * const result = await mutate(id); + * if (result) { + * console.log('Deleted successfully'); + * } + * }; * * if (isLoading) return
Deleting...
; - * if (error) return
Error: {error.message}
; + * if (isError) return
Error: {error?.message}
; * return ; * } * ``` diff --git a/src/hooks/useUpdateVariable.ts b/src/hooks/useUpdateVariable.ts index 0e5019f..bdfdfc9 100644 --- a/src/hooks/useUpdateVariable.ts +++ b/src/hooks/useUpdateVariable.ts @@ -14,17 +14,33 @@ import { mutator } from 'api/mutator' * @remarks * The hook returns a `mutate` function to trigger the update with given payload and exposes state flags. * + * ## Return Value + * + * The `mutate` function returns `Promise`: + * - On success: Returns the API response data + * - On error: Returns `undefined` (error stored in `error` state) + * + * Use `isSuccess`/`isError` flags or check the return value to handle results. + * + * @returns MutationResult with `mutate`, status flags (`isLoading`, `isSuccess`, `isError`), + * `data` (API response), and `error` (if failed). + * * @example * ```tsx * import { useUpdateVariable } from '@figma-vars/hooks'; * * function UpdateVariableButton({ id }: { id: string }) { - * const { mutate, isLoading, error } = useUpdateVariable(); + * const { mutate, isLoading, isError, error } = useUpdateVariable(); * - * const onUpdate = () => mutate({ variableId: id, payload: { name: 'new-name' } }); + * const onUpdate = async () => { + * const result = await mutate({ variableId: id, payload: { name: 'new-name' } }); + * if (result) { + * console.log('Updated successfully'); + * } + * }; * * if (isLoading) return
Updating...
; - * if (error) return
Error: {error.message}
; + * if (isError) return
Error: {error?.message}
; * return ; * } * ``` diff --git a/src/types/mutations.ts b/src/types/mutations.ts index 61b0bf1..34af30d 100644 --- a/src/types/mutations.ts +++ b/src/types/mutations.ts @@ -310,8 +310,15 @@ export interface MutationState { */ export interface MutationOptions { /** - * If true, errors will be rethrown instead of being caught and stored in state. - * This allows callers to use try/catch for error handling. + * Controls error handling behavior for the mutation. + * + * - **`false` (default)**: Errors are caught and stored in the `error` state. + * The `mutate` function returns `undefined` on error. + * Use the `isError` flag and `error` state to handle failures reactively. + * + * - **`true`**: Errors are rethrown, allowing try/catch error handling. + * The `mutate` function throws on error. + * Use this when you need imperative error handling. * * @default false */ @@ -322,7 +329,46 @@ export interface MutationOptions { * Return value of mutation hooks. * * @remarks - * Combines mutation state with a `mutate` trigger function accepting a payload, along with convenient booleans for status. + * Combines mutation state with a `mutate` trigger function accepting a payload, + * along with convenient booleans for status checking. + * + * ## Return Value Semantics + * + * The `mutate` function returns `Promise`: + * + * - **On success**: Returns the mutation result data (`TData`) + * - **On error with `throwOnError: false` (default)**: Returns `undefined` and stores error in `error` state + * - **On error with `throwOnError: true`**: Throws the error (use try/catch) + * + * ## Recommended Patterns + * + * ```ts + * // Pattern 1: Check return value (when throwOnError is false) + * const result = await mutate(payload); + * if (result === undefined) { + * // Check error state + * console.error('Mutation failed:', error); + * } else { + * // Use result + * console.log('Created:', result); + * } + * + * // Pattern 2: Use try/catch (when throwOnError is true) + * try { + * const result = await mutate(payload); + * console.log('Created:', result); + * } catch (err) { + * console.error('Mutation failed:', err); + * } + * + * // Pattern 3: Use status flags (reactive) + * if (isSuccess) { + * console.log('Created:', data); + * } + * if (isError) { + * console.error('Failed:', error); + * } + * ``` * * @typeParam TData - The type of data returned by the mutation. * @typeParam TPayload - The payload type accepted by the mutation trigger. @@ -330,11 +376,23 @@ export interface MutationOptions { * @public */ export interface MutationResult { + /** + * Trigger the mutation with the given payload. + * + * @returns Promise resolving to the mutation result, or `undefined` if an error occurred + * and `throwOnError` is false. When `throwOnError` is true, errors are thrown instead. + */ mutate: (payload: TPayload) => Promise + /** Current mutation status: 'idle' | 'loading' | 'success' | 'error' */ status: 'idle' | 'loading' | 'success' | 'error' + /** The result data from a successful mutation, or `null` if not yet successful. */ data: TData | null + /** The error from a failed mutation, or `null` if no error. */ error: Error | null + /** `true` while the mutation is in progress. */ isLoading: boolean + /** `true` after a successful mutation. */ isSuccess: boolean + /** `true` after a failed mutation. */ isError: boolean } diff --git a/src/utils/retry.ts b/src/utils/retry.ts index a2c4366..2e6f94d 100644 --- a/src/utils/retry.ts +++ b/src/utils/retry.ts @@ -122,7 +122,7 @@ export function withRetry( } // This should never be reached, but TypeScript needs it - /* istanbul ignore next */ + /* c8 ignore next */ throw lastError ?? new Error('Retry failed') } }