Skip to content

Commit 14b1fbf

Browse files
committedApr 8, 2024··
refactor(hooks): pass callbacks data in single object instead of multiple function args
1 parent f628dc7 commit 14b1fbf

File tree

7 files changed

+73
-64
lines changed

7 files changed

+73
-64
lines changed
 

‎packages/example-app/src/app/(examples)/bind-arguments/page.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,25 @@ export default function BindArguments() {
1515
);
1616

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

2121
// You can reset result object by calling `reset`.
2222
// reset();
2323
},
24-
onError(error, input, reset) {
24+
onError({ error, input, reset }) {
2525
console.log("OH NO FROM ONERROR", error, input);
2626

2727
// You can reset result object by calling `reset`.
2828
// reset();
2929
},
30-
onSettled(result, input, reset) {
30+
onSettled({ result, input, reset }) {
3131
console.log("HELLO FROM ONSETTLED", result, input);
3232

3333
// You can reset result object by calling `reset`.
3434
// reset();
3535
},
36-
onExecute(input) {
36+
onExecute({ input }) {
3737
console.log("HELLO FROM ONEXECUTE", input);
3838
},
3939
});

‎packages/example-app/src/app/(examples)/hook/page.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,25 @@ import { deleteUser } from "./deleteuser-action";
1010
export default function Hook() {
1111
// Safe action (`deleteUser`) and optional callbacks passed to `useAction` hook.
1212
const { execute, result, status, reset } = useAction(deleteUser, {
13-
onSuccess(data, input, reset) {
13+
onSuccess({ data, input, reset }) {
1414
console.log("HELLO FROM ONSUCCESS", data, input);
1515

1616
// You can reset result object by calling `reset`.
1717
// reset();
1818
},
19-
onError(error, input, reset) {
19+
onError({ error, input, reset }) {
2020
console.log("OH NO FROM ONERROR", error, input);
2121

2222
// You can reset result object by calling `reset`.
2323
// reset();
2424
},
25-
onSettled(result, input, reset) {
25+
onSettled({ result, input, reset }) {
2626
console.log("HELLO FROM ONSETTLED", result, input);
2727

2828
// You can reset result object by calling `reset`.
2929
// reset();
3030
},
31-
onExecute(input) {
31+
onExecute({ input }) {
3232
console.log("HELLO FROM ONEXECUTE", input);
3333
},
3434
});

‎packages/example-app/src/app/(examples)/optimistic-hook/addlikes-form.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,25 @@ const AddLikesForm = ({ likesCount }: Props) => {
2020
likesCount: likesCount + incrementBy,
2121
}),
2222
{
23-
onSuccess(data, input, reset) {
23+
onSuccess({ data, input, reset }) {
2424
console.log("HELLO FROM ONSUCCESS", data, input);
2525

2626
// You can reset result object by calling `reset`.
2727
// reset();
2828
},
29-
onError(error, input, reset) {
29+
onError({ error, input, reset }) {
3030
console.log("OH NO FROM ONERROR", error, input);
3131

3232
// You can reset result object by calling `reset`.
3333
// reset();
3434
},
35-
onSettled(result, input, reset) {
35+
onSettled({ result, input, reset }) {
3636
console.log("HELLO FROM ONSETTLED", result, input);
3737

3838
// You can reset result object by calling `reset`.
3939
// reset();
4040
},
41-
onExecute(input) {
41+
onExecute({ input }) {
4242
console.log("HELLO FROM ONEXECUTE", input);
4343
},
4444
}

‎packages/next-safe-action/src/hooks.ts

+29-20
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,13 @@ const getActionStatus = <
2222
const S extends Schema,
2323
const BAS extends Schema[],
2424
const Data,
25-
>(
26-
isExecuting: boolean,
27-
result: HookResult<ServerError, S, BAS, Data>
28-
): HookActionStatus => {
25+
>({
26+
isExecuting,
27+
result,
28+
}: {
29+
isExecuting: boolean;
30+
result: HookResult<ServerError, S, BAS, Data>;
31+
}): HookActionStatus => {
2932
if (isExecuting) {
3033
return "executing";
3134
} else if (typeof result.data !== "undefined") {
@@ -47,13 +50,19 @@ const useActionCallbacks = <
4750
const S extends Schema,
4851
const BAS extends Schema[],
4952
const Data,
50-
>(
51-
result: HookResult<ServerError, S, BAS, Data>,
52-
input: InferIn<S>,
53-
status: HookActionStatus,
54-
reset: () => void,
55-
cb?: HookCallbacks<ServerError, S, BAS, Data>
56-
) => {
53+
>({
54+
result,
55+
input,
56+
status,
57+
reset,
58+
cb,
59+
}: {
60+
result: HookResult<ServerError, S, BAS, Data>;
61+
input: InferIn<S>;
62+
status: HookActionStatus;
63+
reset: () => void;
64+
cb?: HookCallbacks<ServerError, S, BAS, Data>;
65+
}) => {
5766
const onExecuteRef = React.useRef(cb?.onExecute);
5867
const onSuccessRef = React.useRef(cb?.onSuccess);
5968
const onErrorRef = React.useRef(cb?.onError);
@@ -69,15 +78,15 @@ const useActionCallbacks = <
6978
const executeCallbacks = async () => {
7079
switch (status) {
7180
case "executing":
72-
await Promise.resolve(onExecute?.(input));
81+
await Promise.resolve(onExecute?.({ input }));
7382
break;
7483
case "hasSucceeded":
75-
await Promise.resolve(onSuccess?.(result.data!, input, reset));
76-
await Promise.resolve(onSettled?.(result, input, reset));
84+
await Promise.resolve(onSuccess?.({ data: result.data!, input, reset }));
85+
await Promise.resolve(onSettled?.({ result, input, reset }));
7786
break;
7887
case "hasErrored":
79-
await Promise.resolve(onError?.(result, input, reset));
80-
await Promise.resolve(onSettled?.(result, input, reset));
88+
await Promise.resolve(onError?.({ error: result, input, reset }));
89+
await Promise.resolve(onSettled?.({ result, input, reset }));
8190
break;
8291
}
8392
};
@@ -109,7 +118,7 @@ export const useAction = <
109118
const [input, setInput] = React.useState<InferIn<S>>();
110119
const [isExecuting, setIsExecuting] = React.useState(false);
111120

112-
const status = getActionStatus<ServerError, S, BAS, Data>(isExecuting, result);
121+
const status = getActionStatus<ServerError, S, BAS, Data>({ isExecuting, result });
113122

114123
const execute = React.useCallback(
115124
(input: InferIn<S>) => {
@@ -138,7 +147,7 @@ export const useAction = <
138147
setResult(DEFAULT_RESULT);
139148
}, []);
140149

141-
useActionCallbacks(result, input, status, reset, callbacks);
150+
useActionCallbacks({ result, input, status, reset, cb: callbacks });
142151

143152
return {
144153
execute,
@@ -180,7 +189,7 @@ export const useOptimisticAction = <
180189
reducer
181190
);
182191

183-
const status = getActionStatus<ServerError, S, BAS, Data>(isExecuting, result);
192+
const status = getActionStatus<ServerError, S, BAS, Data>({ isExecuting, result });
184193

185194
const execute = React.useCallback(
186195
(input: InferIn<S>) => {
@@ -210,7 +219,7 @@ export const useOptimisticAction = <
210219
setResult(DEFAULT_RESULT);
211220
}, []);
212221

213-
useActionCallbacks(result, input, status, reset, callbacks);
222+
useActionCallbacks({ result, input, status, reset, cb: callbacks });
214223

215224
return {
216225
execute,

‎packages/next-safe-action/src/hooks.types.ts

+12-12
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,18 @@ export type HookResult<
1818
* Type of hooks callbacks. These are executed when action is in a specific state.
1919
*/
2020
export type HookCallbacks<ServerError, S extends Schema, BAS extends Schema[], Data> = {
21-
onExecute?: (input: InferIn<S>) => MaybePromise<void>;
22-
onSuccess?: (data: Data, input: InferIn<S>, reset: () => void) => MaybePromise<void>;
23-
onError?: (
24-
error: Omit<HookResult<ServerError, S, BAS, Data>, "data">,
25-
input: InferIn<S>,
26-
reset: () => void
27-
) => MaybePromise<void>;
28-
onSettled?: (
29-
result: HookResult<ServerError, S, BAS, Data>,
30-
input: InferIn<S>,
31-
reset: () => void
32-
) => MaybePromise<void>;
21+
onExecute?: (args: { input: InferIn<S> }) => MaybePromise<void>;
22+
onSuccess?: (args: { data: Data; input: InferIn<S>; reset: () => void }) => MaybePromise<void>;
23+
onError?: (args: {
24+
error: Omit<HookResult<ServerError, S, BAS, Data>, "data">;
25+
input: InferIn<S>;
26+
reset: () => void;
27+
}) => MaybePromise<void>;
28+
onSettled?: (args: {
29+
result: HookResult<ServerError, S, BAS, Data>;
30+
input: InferIn<S>;
31+
reset: () => void;
32+
}) => MaybePromise<void>;
3333
};
3434

3535
/**

‎website/docs/types.md

+12-12
Original file line numberDiff line numberDiff line change
@@ -143,18 +143,18 @@ Type of hooks callbacks. These are executed when action is in a specific state.
143143

144144
```typescript
145145
export type HookCallbacks<ServerError, S extends Schema, BAS extends Schema[], Data> = {
146-
onExecute?: (input: InferIn<S>) => MaybePromise<void>;
147-
onSuccess?: (data: Data, input: InferIn<S>, reset: () => void) => MaybePromise<void>;
148-
onError?: (
149-
error: Omit<HookResult<ServerError, S, BAS, Data>, "data">,
150-
input: InferIn<S>,
151-
reset: () => void
152-
) => MaybePromise<void>;
153-
onSettled?: (
154-
result: HookResult<ServerError, S, BAS, Data>,
155-
input: InferIn<S>,
156-
reset: () => void
157-
) => MaybePromise<void>;
146+
onExecute?: (args: { input: InferIn<S> }) => MaybePromise<void>;
147+
onSuccess?: (args: { data: Data; input: InferIn<S>; reset: () => void }) => MaybePromise<void>;
148+
onError?: (args: {
149+
error: Omit<HookResult<ServerError, S, BAS, Data>, "data">;
150+
input: InferIn<S>;
151+
reset: () => void;
152+
}) => MaybePromise<void>;
153+
onSettled?: (args: {
154+
result: HookResult<ServerError, S, BAS, Data>;
155+
input: InferIn<S>;
156+
reset: () => void;
157+
}) => MaybePromise<void>;
158158
};
159159
```
160160

‎website/docs/usage/client-components/hooks/callbacks.md

+8-8
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,18 @@ Hook callbacks are a way to perform custom logic based on the current action exe
99

1010
```tsx
1111
const action = useAction(testAction, {
12-
onExecute: (input) => {},
13-
onSuccess: (data, input, reset) => {},
14-
onError: (error, input, reset) => {},
15-
onSettled: (result, input, reset) => {},
12+
onExecute: ({ input }) => {},
13+
onSuccess: ({ data, input, reset }) => {},
14+
onError: ({ error, input, reset }) => {},
15+
onSettled: ({ result, input, reset }) => {},
1616
});
1717
```
1818

1919
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):
2020

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

0 commit comments

Comments
 (0)
Please sign in to comment.