Skip to content

Commit

Permalink
refactor(hooks): pass callbacks data in single object instead of mult…
Browse files Browse the repository at this point in the history
…iple function args
  • Loading branch information
TheEdoRan committed Apr 8, 2024
1 parent f628dc7 commit 14b1fbf
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,25 @@ export default function BindArguments() {
);

const { execute, result, status, reset } = useAction(boundOnboardUser, {
onSuccess(data, input, reset) {
onSuccess({ data, input, reset }) {
console.log("HELLO FROM ONSUCCESS", data, input);

// You can reset result object by calling `reset`.
// reset();
},
onError(error, input, reset) {
onError({ error, input, reset }) {
console.log("OH NO FROM ONERROR", error, input);

// You can reset result object by calling `reset`.
// reset();
},
onSettled(result, input, reset) {
onSettled({ result, input, reset }) {
console.log("HELLO FROM ONSETTLED", result, input);

// You can reset result object by calling `reset`.
// reset();
},
onExecute(input) {
onExecute({ input }) {
console.log("HELLO FROM ONEXECUTE", input);
},
});
Expand Down
8 changes: 4 additions & 4 deletions packages/example-app/src/app/(examples)/hook/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,25 @@ import { deleteUser } from "./deleteuser-action";
export default function Hook() {
// Safe action (`deleteUser`) and optional callbacks passed to `useAction` hook.
const { execute, result, status, reset } = useAction(deleteUser, {
onSuccess(data, input, reset) {
onSuccess({ data, input, reset }) {
console.log("HELLO FROM ONSUCCESS", data, input);

// You can reset result object by calling `reset`.
// reset();
},
onError(error, input, reset) {
onError({ error, input, reset }) {
console.log("OH NO FROM ONERROR", error, input);

// You can reset result object by calling `reset`.
// reset();
},
onSettled(result, input, reset) {
onSettled({ result, input, reset }) {
console.log("HELLO FROM ONSETTLED", result, input);

// You can reset result object by calling `reset`.
// reset();
},
onExecute(input) {
onExecute({ input }) {
console.log("HELLO FROM ONEXECUTE", input);
},
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,25 @@ const AddLikesForm = ({ likesCount }: Props) => {
likesCount: likesCount + incrementBy,
}),
{
onSuccess(data, input, reset) {
onSuccess({ data, input, reset }) {
console.log("HELLO FROM ONSUCCESS", data, input);

// You can reset result object by calling `reset`.
// reset();
},
onError(error, input, reset) {
onError({ error, input, reset }) {
console.log("OH NO FROM ONERROR", error, input);

// You can reset result object by calling `reset`.
// reset();
},
onSettled(result, input, reset) {
onSettled({ result, input, reset }) {
console.log("HELLO FROM ONSETTLED", result, input);

// You can reset result object by calling `reset`.
// reset();
},
onExecute(input) {
onExecute({ input }) {
console.log("HELLO FROM ONEXECUTE", input);
},
}
Expand Down
49 changes: 29 additions & 20 deletions packages/next-safe-action/src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,13 @@ const getActionStatus = <
const S extends Schema,
const BAS extends Schema[],
const Data,
>(
isExecuting: boolean,
result: HookResult<ServerError, S, BAS, Data>
): HookActionStatus => {
>({
isExecuting,
result,
}: {
isExecuting: boolean;
result: HookResult<ServerError, S, BAS, Data>;
}): HookActionStatus => {
if (isExecuting) {
return "executing";
} else if (typeof result.data !== "undefined") {
Expand All @@ -47,13 +50,19 @@ const useActionCallbacks = <
const S extends Schema,
const BAS extends Schema[],
const Data,
>(
result: HookResult<ServerError, S, BAS, Data>,
input: InferIn<S>,
status: HookActionStatus,
reset: () => void,
cb?: HookCallbacks<ServerError, S, BAS, Data>
) => {
>({
result,
input,
status,
reset,
cb,
}: {
result: HookResult<ServerError, S, BAS, Data>;
input: InferIn<S>;
status: HookActionStatus;
reset: () => void;
cb?: HookCallbacks<ServerError, S, BAS, Data>;
}) => {
const onExecuteRef = React.useRef(cb?.onExecute);
const onSuccessRef = React.useRef(cb?.onSuccess);
const onErrorRef = React.useRef(cb?.onError);
Expand All @@ -69,15 +78,15 @@ const useActionCallbacks = <
const executeCallbacks = async () => {
switch (status) {
case "executing":
await Promise.resolve(onExecute?.(input));
await Promise.resolve(onExecute?.({ input }));
break;
case "hasSucceeded":
await Promise.resolve(onSuccess?.(result.data!, input, reset));
await Promise.resolve(onSettled?.(result, input, reset));
await Promise.resolve(onSuccess?.({ data: result.data!, input, reset }));
await Promise.resolve(onSettled?.({ result, input, reset }));
break;
case "hasErrored":
await Promise.resolve(onError?.(result, input, reset));
await Promise.resolve(onSettled?.(result, input, reset));
await Promise.resolve(onError?.({ error: result, input, reset }));
await Promise.resolve(onSettled?.({ result, input, reset }));
break;
}
};
Expand Down Expand Up @@ -109,7 +118,7 @@ export const useAction = <
const [input, setInput] = React.useState<InferIn<S>>();
const [isExecuting, setIsExecuting] = React.useState(false);

const status = getActionStatus<ServerError, S, BAS, Data>(isExecuting, result);
const status = getActionStatus<ServerError, S, BAS, Data>({ isExecuting, result });

const execute = React.useCallback(
(input: InferIn<S>) => {
Expand Down Expand Up @@ -138,7 +147,7 @@ export const useAction = <
setResult(DEFAULT_RESULT);
}, []);

useActionCallbacks(result, input, status, reset, callbacks);
useActionCallbacks({ result, input, status, reset, cb: callbacks });

return {
execute,
Expand Down Expand Up @@ -180,7 +189,7 @@ export const useOptimisticAction = <
reducer
);

const status = getActionStatus<ServerError, S, BAS, Data>(isExecuting, result);
const status = getActionStatus<ServerError, S, BAS, Data>({ isExecuting, result });

const execute = React.useCallback(
(input: InferIn<S>) => {
Expand Down Expand Up @@ -210,7 +219,7 @@ export const useOptimisticAction = <
setResult(DEFAULT_RESULT);
}, []);

useActionCallbacks(result, input, status, reset, callbacks);
useActionCallbacks({ result, input, status, reset, cb: callbacks });

return {
execute,
Expand Down
24 changes: 12 additions & 12 deletions packages/next-safe-action/src/hooks.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,18 @@ export type HookResult<
* Type of hooks callbacks. These are executed when action is in a specific state.
*/
export type HookCallbacks<ServerError, S extends Schema, BAS extends Schema[], Data> = {
onExecute?: (input: InferIn<S>) => MaybePromise<void>;
onSuccess?: (data: Data, input: InferIn<S>, reset: () => void) => MaybePromise<void>;
onError?: (
error: Omit<HookResult<ServerError, S, BAS, Data>, "data">,
input: InferIn<S>,
reset: () => void
) => MaybePromise<void>;
onSettled?: (
result: HookResult<ServerError, S, BAS, Data>,
input: InferIn<S>,
reset: () => void
) => MaybePromise<void>;
onExecute?: (args: { input: InferIn<S> }) => MaybePromise<void>;
onSuccess?: (args: { data: Data; input: InferIn<S>; reset: () => void }) => MaybePromise<void>;
onError?: (args: {
error: Omit<HookResult<ServerError, S, BAS, Data>, "data">;
input: InferIn<S>;
reset: () => void;
}) => MaybePromise<void>;
onSettled?: (args: {
result: HookResult<ServerError, S, BAS, Data>;
input: InferIn<S>;
reset: () => void;
}) => MaybePromise<void>;
};

/**
Expand Down
24 changes: 12 additions & 12 deletions website/docs/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,18 +143,18 @@ Type of hooks callbacks. These are executed when action is in a specific state.

```typescript
export type HookCallbacks<ServerError, S extends Schema, BAS extends Schema[], Data> = {
onExecute?: (input: InferIn<S>) => MaybePromise<void>;
onSuccess?: (data: Data, input: InferIn<S>, reset: () => void) => MaybePromise<void>;
onError?: (
error: Omit<HookResult<ServerError, S, BAS, Data>, "data">,
input: InferIn<S>,
reset: () => void
) => MaybePromise<void>;
onSettled?: (
result: HookResult<ServerError, S, BAS, Data>,
input: InferIn<S>,
reset: () => void
) => MaybePromise<void>;
onExecute?: (args: { input: InferIn<S> }) => MaybePromise<void>;
onSuccess?: (args: { data: Data; input: InferIn<S>; reset: () => void }) => MaybePromise<void>;
onError?: (args: {
error: Omit<HookResult<ServerError, S, BAS, Data>, "data">;
input: InferIn<S>;
reset: () => void;
}) => MaybePromise<void>;
onSettled?: (args: {
result: HookResult<ServerError, S, BAS, Data>;
input: InferIn<S>;
reset: () => void;
}) => MaybePromise<void>;
};
```

Expand Down
16 changes: 8 additions & 8 deletions website/docs/usage/client-components/hooks/callbacks.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@ Hook callbacks are a way to perform custom logic based on the current action exe

```tsx
const action = useAction(testAction, {
onExecute: (input) => {},
onSuccess: (data, input, reset) => {},
onError: (error, input, reset) => {},
onSettled: (result, input, reset) => {},
onExecute: ({ input }) => {},
onSuccess: ({ data, input, reset }) => {},
onError: ({ error, input, reset }) => {},
onSettled: ({ result, input, reset }) => {},
});
```

Here is the full list of callbacks, with their behavior explained. All of them are optional and have return type `void` or `Promise<void>` (async or non-async functions with no return):

| Name | [`HookActionStatus`](/docs/types#hookactionstatus) state | Arguments |
|--------------|------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------|
| `onExecute?` | `"executing"` | `input: InferIn<S>` |
| `onSuccess?` | `"hasSucceeded"` | `data: Data`,<br/> `input: InferIn<S>`,<br/> `reset: () => void` |
| `onError?` | `"hasErrored"` | `error: Omit<HookResult<S, Data>, "data">`,<br/> `input: InferIn<S>`,<br/> `reset: () => void` |
| `onSettled?` | `"hasSucceeded"` or `"hasErrored"` (after `onSuccess` and/or `onError`) | `result: HookResult<S, Data>`,<br/> `input: InferIn<S>`,<br/> `reset: () => void` |
| `onExecute?` | `"executing"` | `{ input: InferIn<S> }` |
| `onSuccess?` | `"hasSucceeded"` | `{ data: Data`,<br/> `input: InferIn<S>`,<br/> `reset: () => void }` |
| `onError?` | `"hasErrored"` | `{ error: Omit<HookResult<S, Data>, "data">`,<br/> `input: InferIn<S>`,<br/> `reset: () => void }` |
| `onSettled?` | `"hasSucceeded"` or `"hasErrored"` (after `onSuccess` and/or `onError`) | `{ result: HookResult<S, Data>`,<br/> `input: InferIn<S>`,<br/> `reset: () => void }` |

0 comments on commit 14b1fbf

Please sign in to comment.