Skip to content

Commit

Permalink
refactor(hooks): move hooks utils to separate file
Browse files Browse the repository at this point in the history
  • Loading branch information
TheEdoRan committed Jun 13, 2024
1 parent 4ead815 commit 3955704
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 93 deletions.
96 changes: 96 additions & 0 deletions packages/next-safe-action/src/hooks-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import type { InferIn, Schema } from "@typeschema/main";
import * as React from "react";
import {} from "react/experimental";
import type {} from "zod";
import type { HookActionStatus, HookCallbacks, HookResult } from "./hooks.types";

export const getActionStatus = <
ServerError,
S extends Schema | undefined,
const BAS extends readonly Schema[],
CVE,
CBAVE,
Data,
>({
isIdle,
isExecuting,
result,
}: {
isIdle: boolean;
isExecuting: boolean;
result: HookResult<ServerError, S, BAS, CVE, CBAVE, Data>;
}): HookActionStatus => {
if (isIdle) {
return "idle";
} else if (isExecuting) {
return "executing";
} else if (
typeof result.validationErrors !== "undefined" ||
typeof result.bindArgsValidationErrors !== "undefined" ||
typeof result.serverError !== "undefined" ||
typeof result.fetchError !== "undefined"
) {
return "hasErrored";
} else {
return "hasSucceeded";
}
};

export const getActionShorthandStatusObject = (status: HookActionStatus) => {
return {
isIdle: status === "idle",
isExecuting: status === "executing",
hasSucceeded: status === "hasSucceeded",
hasErrored: status === "hasErrored",
};
};

export const useActionCallbacks = <
ServerError,
S extends Schema | undefined,
const BAS extends readonly Schema[],
CVE,
CBAVE,
Data,
>({
result,
input,
status,
cb,
}: {
result: HookResult<ServerError, S, BAS, CVE, CBAVE, Data>;
input: S extends Schema ? InferIn<S> : undefined;
status: HookActionStatus;
cb?: HookCallbacks<ServerError, S, BAS, CVE, CBAVE, Data>;
}) => {
const onExecuteRef = React.useRef(cb?.onExecute);
const onSuccessRef = React.useRef(cb?.onSuccess);
const onErrorRef = React.useRef(cb?.onError);
const onSettledRef = React.useRef(cb?.onSettled);

// Execute the callback when the action status changes.
React.useEffect(() => {
const onExecute = onExecuteRef.current;
const onSuccess = onSuccessRef.current;
const onError = onErrorRef.current;
const onSettled = onSettledRef.current;

const executeCallbacks = async () => {
switch (status) {
case "executing":
await Promise.resolve(onExecute?.({ input }));
break;
case "hasSucceeded":
await Promise.resolve(onSuccess?.({ data: result?.data, input }));
await Promise.resolve(onSettled?.({ result, input }));
break;
case "hasErrored":
await Promise.resolve(onError?.({ error: result, input }));
await Promise.resolve(onSettled?.({ result, input }));
break;
}
};

executeCallbacks().catch(console.error);
}, [status, result, input]);
};
94 changes: 2 additions & 92 deletions packages/next-safe-action/src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,100 +7,10 @@ import * as React from "react";
import * as ReactDOM from "react-dom";
import {} from "react/experimental";
import type {} from "zod";
import type { HookActionStatus, HookCallbacks, HookResult, HookSafeActionFn } from "./hooks.types";
import { getActionShorthandStatusObject, getActionStatus, useActionCallbacks } from "./hooks-utils";
import type { HookCallbacks, HookResult, HookSafeActionFn } from "./hooks.types";
import { isError } from "./utils";

export const getActionStatus = <
ServerError,
S extends Schema | undefined,
const BAS extends readonly Schema[],
CVE,
CBAVE,
Data,
>({
isIdle,
isExecuting,
result,
}: {
isIdle: boolean;
isExecuting: boolean;
result: HookResult<ServerError, S, BAS, CVE, CBAVE, Data>;
}): HookActionStatus => {
if (isIdle) {
return "idle";
} else if (isExecuting) {
return "executing";
} else if (
typeof result.validationErrors !== "undefined" ||
typeof result.bindArgsValidationErrors !== "undefined" ||
typeof result.serverError !== "undefined" ||
typeof result.fetchError !== "undefined"
) {
return "hasErrored";
} else {
return "hasSucceeded";
}
};

export const getActionShorthandStatusObject = (status: HookActionStatus) => {
return {
isIdle: status === "idle",
isExecuting: status === "executing",
hasSucceeded: status === "hasSucceeded",
hasErrored: status === "hasErrored",
};
};

export const useActionCallbacks = <
ServerError,
S extends Schema | undefined,
const BAS extends readonly Schema[],
CVE,
CBAVE,
Data,
>({
result,
input,
status,
cb,
}: {
result: HookResult<ServerError, S, BAS, CVE, CBAVE, Data>;
input: S extends Schema ? InferIn<S> : undefined;
status: HookActionStatus;
cb?: HookCallbacks<ServerError, S, BAS, CVE, CBAVE, Data>;
}) => {
const onExecuteRef = React.useRef(cb?.onExecute);
const onSuccessRef = React.useRef(cb?.onSuccess);
const onErrorRef = React.useRef(cb?.onError);
const onSettledRef = React.useRef(cb?.onSettled);

// Execute the callback when the action status changes.
React.useEffect(() => {
const onExecute = onExecuteRef.current;
const onSuccess = onSuccessRef.current;
const onError = onErrorRef.current;
const onSettled = onSettledRef.current;

const executeCallbacks = async () => {
switch (status) {
case "executing":
await Promise.resolve(onExecute?.({ input }));
break;
case "hasSucceeded":
await Promise.resolve(onSuccess?.({ data: result?.data, input }));
await Promise.resolve(onSettled?.({ result, input }));
break;
case "hasErrored":
await Promise.resolve(onError?.({ error: result, input }));
await Promise.resolve(onSettled?.({ result, input }));
break;
}
};

executeCallbacks().catch(console.error);
}, [status, result, input]);
};

// HOOKS

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/next-safe-action/src/stateful-hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as React from "react";
import * as ReactDOM from "react-dom";
import {} from "react/experimental";
import type {} from "zod";
import { getActionShorthandStatusObject, getActionStatus, useActionCallbacks } from "./hooks";
import { getActionShorthandStatusObject, getActionStatus, useActionCallbacks } from "./hooks-utils";
import type { HookCallbacks, HookSafeStateActionFn } from "./hooks.types";
/**
* Use the stateful action from a Client Component via hook. Used for actions defined with [`stateAction`](https://next-safe-action.dev/docs/safe-action-client/instance-methods#action--stateaction).
Expand Down

0 comments on commit 3955704

Please sign in to comment.