next-safe-action
Version:
Type safe and validated Server Actions in your Next.js project.
244 lines (240 loc) • 6.12 kB
JavaScript
"use client";
import { t as FrameworkErrorHandler } from "./errors-BsNXg-5Q.mjs";
import {
n as getActionStatus,
r as useActionCallbacks,
t as getActionShorthandStatusObject,
} from "./hooks-utils-DA2xDVWH.mjs";
import * as React from "react";
//#region src/hooks.ts
/**
* Use the action from a Client Component via hook.
* @param safeActionFn The action function
* @param cb Optional base utils and callbacks
*
* {@link https://next-safe-action.dev/docs/execute-actions/hooks/useaction See docs for more information}
*/
const useAction = (safeActionFn, cb) => {
const [isTransitioning, startTransition] = React.useTransition();
const [result, setResult] = React.useState({});
const [clientInput, setClientInput] = React.useState();
const [isExecuting, setIsExecuting] = React.useState(false);
const [navigationError, setNavigationError] = React.useState(null);
const [thrownError, setThrownError] = React.useState(null);
const [isIdle, setIsIdle] = React.useState(true);
const status = getActionStatus({
isExecuting,
isTransitioning,
result,
isIdle,
hasNavigated: navigationError !== null,
hasThrownError: thrownError !== null,
});
const execute = React.useCallback(
(input) => {
setTimeout(() => {
setIsIdle(false);
setNavigationError(null);
setThrownError(null);
setClientInput(input);
setIsExecuting(true);
}, 0);
startTransition(() => {
safeActionFn(input)
.then((res) => setResult(res ?? {}))
.catch((e) => {
setResult({});
if (FrameworkErrorHandler.isNavigationError(e)) {
setNavigationError(e);
throw e;
}
setThrownError(e);
throw e;
})
.finally(() => {
setIsExecuting(false);
});
});
},
[safeActionFn]
);
const executeAsync = React.useCallback(
(input) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
setIsIdle(false);
setNavigationError(null);
setThrownError(null);
setClientInput(input);
setIsExecuting(true);
}, 0);
startTransition(() => {
safeActionFn(input)
.then((res) => {
setResult(res ?? {});
resolve(res);
})
.catch((e) => {
setResult({});
if (FrameworkErrorHandler.isNavigationError(e)) {
setNavigationError(e);
throw e;
}
setThrownError(e);
reject(e);
})
.finally(() => {
setIsExecuting(false);
});
});
});
},
[safeActionFn]
);
const reset = React.useCallback(() => {
setIsIdle(true);
setNavigationError(null);
setThrownError(null);
setClientInput(void 0);
setResult({});
}, []);
useActionCallbacks({
result: result ?? {},
input: clientInput,
status,
navigationError,
thrownError,
cb,
});
return {
execute,
executeAsync,
input: clientInput,
result,
reset,
status,
...getActionShorthandStatusObject(status),
};
};
/**
* Use the action from a Client Component via hook, with optimistic data update.
* @param safeActionFn The action function
* @param utils Required `currentData` and `updateFn` and optional callbacks
*
* {@link https://next-safe-action.dev/docs/execute-actions/hooks/useoptimisticaction See docs for more information}
*/
const useOptimisticAction = (safeActionFn, utils) => {
const [isTransitioning, startTransition] = React.useTransition();
const [result, setResult] = React.useState({});
const [clientInput, setClientInput] = React.useState();
const [isExecuting, setIsExecuting] = React.useState(false);
const [navigationError, setNavigationError] = React.useState(null);
const [thrownError, setThrownError] = React.useState(null);
const [isIdle, setIsIdle] = React.useState(true);
const [optimisticState, setOptimisticValue] = React.useOptimistic(utils.currentState, utils.updateFn);
const status = getActionStatus({
isExecuting,
isTransitioning,
result,
isIdle,
hasNavigated: navigationError !== null,
hasThrownError: thrownError !== null,
});
const execute = React.useCallback(
(input) => {
setTimeout(() => {
setIsIdle(false);
setClientInput(input);
setNavigationError(null);
setThrownError(null);
setIsExecuting(true);
}, 0);
startTransition(() => {
setOptimisticValue(input);
safeActionFn(input)
.then((res) => setResult(res ?? {}))
.catch((e) => {
setResult({});
if (FrameworkErrorHandler.isNavigationError(e)) {
setNavigationError(e);
throw e;
}
setThrownError(e);
throw e;
})
.finally(() => {
setIsExecuting(false);
});
});
},
[safeActionFn, setOptimisticValue]
);
const executeAsync = React.useCallback(
(input) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
setIsIdle(false);
setClientInput(input);
setNavigationError(null);
setThrownError(null);
setIsExecuting(true);
}, 0);
startTransition(() => {
setOptimisticValue(input);
safeActionFn(input)
.then((res) => {
setResult(res ?? {});
resolve(res);
})
.catch((e) => {
setResult({});
if (FrameworkErrorHandler.isNavigationError(e)) {
setNavigationError(e);
throw e;
}
setThrownError(e);
reject(e);
})
.finally(() => {
setIsExecuting(false);
});
});
});
},
[safeActionFn, setOptimisticValue]
);
const reset = React.useCallback(() => {
setIsIdle(true);
setClientInput(void 0);
setNavigationError(null);
setThrownError(null);
setResult({});
}, []);
useActionCallbacks({
result: result ?? {},
input: clientInput,
status,
navigationError,
thrownError,
cb: {
onExecute: utils.onExecute,
onSuccess: utils.onSuccess,
onError: utils.onError,
onSettled: utils.onSettled,
onNavigation: utils.onNavigation,
},
});
return {
execute,
executeAsync,
input: clientInput,
result,
optimisticState,
reset,
status,
...getActionShorthandStatusObject(status),
};
};
//#endregion
export { useAction, useOptimisticAction };
//# sourceMappingURL=hooks.mjs.map