UNPKG

next-safe-action

Version:

Type safe and validated Server Actions in your Next.js project.

1 lines 12.3 kB
{"version":3,"file":"hooks.mjs","names":[],"sources":["../src/hooks.ts"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { getActionShorthandStatusObject, getActionStatus, useActionCallbacks } from \"./hooks-utils\";\nimport type {\n\tHookCallbacks,\n\tHookSafeActionFn,\n\tUseActionHookReturn,\n\tUseOptimisticActionHookReturn,\n} from \"./hooks.types\";\nimport type { SafeActionResult } from \"./index.types\";\nimport { FrameworkErrorHandler } from \"./next/errors\";\nimport type { InferInputOrDefault, StandardSchemaV1 } from \"./standard-schema\";\n\n// HOOKS\n\n/**\n * Use the action from a Client Component via hook.\n * @param safeActionFn The action function\n * @param cb Optional base utils and callbacks\n *\n * {@link https://next-safe-action.dev/docs/execute-actions/hooks/useaction See docs for more information}\n */\nexport const useAction = <ServerError, S extends StandardSchemaV1 | undefined, CVE, Data>(\n\tsafeActionFn: HookSafeActionFn<ServerError, S, CVE, Data>,\n\tcb?: HookCallbacks<ServerError, S, CVE, Data>\n): UseActionHookReturn<ServerError, S, CVE, Data> => {\n\tconst [isTransitioning, startTransition] = React.useTransition();\n\tconst [result, setResult] = React.useState<SafeActionResult<ServerError, S, CVE, Data>>({});\n\tconst [clientInput, setClientInput] = React.useState<InferInputOrDefault<S, void>>();\n\tconst [isExecuting, setIsExecuting] = React.useState(false);\n\tconst [navigationError, setNavigationError] = React.useState<Error | null>(null);\n\tconst [thrownError, setThrownError] = React.useState<Error | null>(null);\n\tconst [isIdle, setIsIdle] = React.useState(true);\n\n\tconst status = getActionStatus<ServerError, S, CVE, Data>({\n\t\tisExecuting,\n\t\tisTransitioning,\n\t\tresult,\n\t\tisIdle,\n\t\thasNavigated: navigationError !== null,\n\t\thasThrownError: thrownError !== null,\n\t});\n\n\tconst execute = React.useCallback(\n\t\t(input: InferInputOrDefault<S, void>) => {\n\t\t\tsetTimeout(() => {\n\t\t\t\tsetIsIdle(false);\n\t\t\t\tsetNavigationError(null);\n\t\t\t\tsetThrownError(null);\n\t\t\t\tsetClientInput(input);\n\t\t\t\tsetIsExecuting(true);\n\t\t\t}, 0);\n\n\t\t\tstartTransition(() => {\n\t\t\t\tsafeActionFn(input as InferInputOrDefault<S, undefined>)\n\t\t\t\t\t.then((res) => setResult(res ?? {}))\n\t\t\t\t\t.catch((e) => {\n\t\t\t\t\t\tsetResult({});\n\n\t\t\t\t\t\tif (FrameworkErrorHandler.isNavigationError(e)) {\n\t\t\t\t\t\t\tsetNavigationError(e);\n\t\t\t\t\t\t\tthrow e;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tsetThrownError(e as Error);\n\t\t\t\t\t\tthrow e;\n\t\t\t\t\t})\n\t\t\t\t\t.finally(() => {\n\t\t\t\t\t\tsetIsExecuting(false);\n\t\t\t\t\t});\n\t\t\t});\n\t\t},\n\t\t[safeActionFn]\n\t);\n\n\tconst executeAsync = React.useCallback(\n\t\t(input: InferInputOrDefault<S, void>) => {\n\t\t\tconst fn = new Promise<Awaited<ReturnType<typeof safeActionFn>>>((resolve, reject) => {\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\tsetIsIdle(false);\n\t\t\t\t\tsetNavigationError(null);\n\t\t\t\t\tsetThrownError(null);\n\t\t\t\t\tsetClientInput(input);\n\t\t\t\t\tsetIsExecuting(true);\n\t\t\t\t}, 0);\n\n\t\t\t\tstartTransition(() => {\n\t\t\t\t\tsafeActionFn(input as InferInputOrDefault<S, undefined>)\n\t\t\t\t\t\t.then((res) => {\n\t\t\t\t\t\t\tsetResult(res ?? {});\n\t\t\t\t\t\t\tresolve(res);\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch((e) => {\n\t\t\t\t\t\t\tsetResult({});\n\n\t\t\t\t\t\t\tif (FrameworkErrorHandler.isNavigationError(e)) {\n\t\t\t\t\t\t\t\tsetNavigationError(e);\n\t\t\t\t\t\t\t\tthrow e;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tsetThrownError(e as Error);\n\t\t\t\t\t\t\treject(e);\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.finally(() => {\n\t\t\t\t\t\t\tsetIsExecuting(false);\n\t\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t});\n\n\t\t\treturn fn;\n\t\t},\n\t\t[safeActionFn]\n\t);\n\n\tconst reset = React.useCallback(() => {\n\t\tsetIsIdle(true);\n\t\tsetNavigationError(null);\n\t\tsetThrownError(null);\n\t\tsetClientInput(undefined);\n\t\tsetResult({});\n\t}, []);\n\n\tuseActionCallbacks({\n\t\tresult: result ?? {},\n\t\tinput: clientInput as InferInputOrDefault<S, undefined>,\n\t\tstatus,\n\t\tnavigationError,\n\t\tthrownError,\n\t\tcb,\n\t});\n\n\treturn {\n\t\texecute,\n\t\texecuteAsync,\n\t\tinput: clientInput as InferInputOrDefault<S, undefined>,\n\t\tresult,\n\t\treset,\n\t\tstatus,\n\t\t...getActionShorthandStatusObject(status),\n\t};\n};\n\n/**\n * Use the action from a Client Component via hook, with optimistic data update.\n * @param safeActionFn The action function\n * @param utils Required `currentData` and `updateFn` and optional callbacks\n *\n * {@link https://next-safe-action.dev/docs/execute-actions/hooks/useoptimisticaction See docs for more information}\n */\nexport const useOptimisticAction = <ServerError, S extends StandardSchemaV1 | undefined, CVE, Data, State>(\n\tsafeActionFn: HookSafeActionFn<ServerError, S, CVE, Data>,\n\tutils: {\n\t\tcurrentState: State;\n\t\tupdateFn: (state: State, input: InferInputOrDefault<S, void>) => State;\n\t} & HookCallbacks<ServerError, S, CVE, Data>\n): UseOptimisticActionHookReturn<ServerError, S, CVE, Data, State> => {\n\tconst [isTransitioning, startTransition] = React.useTransition();\n\tconst [result, setResult] = React.useState<SafeActionResult<ServerError, S, CVE, Data>>({});\n\tconst [clientInput, setClientInput] = React.useState<InferInputOrDefault<S, void>>();\n\tconst [isExecuting, setIsExecuting] = React.useState(false);\n\tconst [navigationError, setNavigationError] = React.useState<Error | null>(null);\n\tconst [thrownError, setThrownError] = React.useState<Error | null>(null);\n\tconst [isIdle, setIsIdle] = React.useState(true);\n\tconst [optimisticState, setOptimisticValue] = React.useOptimistic<State, InferInputOrDefault<S, undefined>>(\n\t\tutils.currentState,\n\t\tutils.updateFn\n\t);\n\n\tconst status = getActionStatus<ServerError, S, CVE, Data>({\n\t\tisExecuting,\n\t\tisTransitioning,\n\t\tresult,\n\t\tisIdle,\n\t\thasNavigated: navigationError !== null,\n\t\thasThrownError: thrownError !== null,\n\t});\n\n\tconst execute = React.useCallback(\n\t\t(input: InferInputOrDefault<S, void>) => {\n\t\t\tsetTimeout(() => {\n\t\t\t\tsetIsIdle(false);\n\t\t\t\tsetClientInput(input);\n\t\t\t\tsetNavigationError(null);\n\t\t\t\tsetThrownError(null);\n\t\t\t\tsetIsExecuting(true);\n\t\t\t}, 0);\n\n\t\t\tstartTransition(() => {\n\t\t\t\tsetOptimisticValue(input as InferInputOrDefault<S, undefined>);\n\t\t\t\tsafeActionFn(input as InferInputOrDefault<S, undefined>)\n\t\t\t\t\t.then((res) => setResult(res ?? {}))\n\t\t\t\t\t.catch((e) => {\n\t\t\t\t\t\tsetResult({});\n\n\t\t\t\t\t\tif (FrameworkErrorHandler.isNavigationError(e)) {\n\t\t\t\t\t\t\tsetNavigationError(e);\n\t\t\t\t\t\t\tthrow e;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tsetThrownError(e as Error);\n\t\t\t\t\t\tthrow e;\n\t\t\t\t\t})\n\t\t\t\t\t.finally(() => {\n\t\t\t\t\t\tsetIsExecuting(false);\n\t\t\t\t\t});\n\t\t\t});\n\t\t},\n\t\t[safeActionFn, setOptimisticValue]\n\t);\n\n\tconst executeAsync = React.useCallback(\n\t\t(input: InferInputOrDefault<S, void>) => {\n\t\t\tconst fn = new Promise<Awaited<ReturnType<typeof safeActionFn>>>((resolve, reject) => {\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\tsetIsIdle(false);\n\t\t\t\t\tsetClientInput(input);\n\t\t\t\t\tsetNavigationError(null);\n\t\t\t\t\tsetThrownError(null);\n\t\t\t\t\tsetIsExecuting(true);\n\t\t\t\t}, 0);\n\n\t\t\t\tstartTransition(() => {\n\t\t\t\t\tsetOptimisticValue(input as InferInputOrDefault<S, undefined>);\n\t\t\t\t\tsafeActionFn(input as InferInputOrDefault<S, undefined>)\n\t\t\t\t\t\t.then((res) => {\n\t\t\t\t\t\t\tsetResult(res ?? {});\n\t\t\t\t\t\t\tresolve(res);\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch((e) => {\n\t\t\t\t\t\t\tsetResult({});\n\n\t\t\t\t\t\t\tif (FrameworkErrorHandler.isNavigationError(e)) {\n\t\t\t\t\t\t\t\tsetNavigationError(e);\n\t\t\t\t\t\t\t\tthrow e;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tsetThrownError(e as Error);\n\t\t\t\t\t\t\treject(e);\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.finally(() => {\n\t\t\t\t\t\t\tsetIsExecuting(false);\n\t\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t});\n\n\t\t\treturn fn;\n\t\t},\n\t\t[safeActionFn, setOptimisticValue]\n\t);\n\n\tconst reset = React.useCallback(() => {\n\t\tsetIsIdle(true);\n\t\tsetClientInput(undefined);\n\t\tsetNavigationError(null);\n\t\tsetThrownError(null);\n\t\tsetResult({});\n\t}, []);\n\n\tuseActionCallbacks({\n\t\tresult: result ?? {},\n\t\tinput: clientInput as InferInputOrDefault<S, undefined>,\n\t\tstatus,\n\t\tnavigationError,\n\t\tthrownError,\n\t\tcb: {\n\t\t\tonExecute: utils.onExecute,\n\t\t\tonSuccess: utils.onSuccess,\n\t\t\tonError: utils.onError,\n\t\t\tonSettled: utils.onSettled,\n\t\t\tonNavigation: utils.onNavigation,\n\t\t},\n\t});\n\n\treturn {\n\t\texecute,\n\t\texecuteAsync,\n\t\tinput: clientInput as InferInputOrDefault<S, undefined>,\n\t\tresult,\n\t\toptimisticState,\n\t\treset,\n\t\tstatus,\n\t\t...getActionShorthandStatusObject(status),\n\t};\n};\n\nexport type * from \"./hooks.types\";\n"],"mappings":"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"}