-
-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: allow actions to have zero or multiple inputs #48
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
resolves #29 |
@@ -118,7 +118,7 @@ const useActionCallbacks = <const S extends Schema, const Data>( | |||
* {@link https://next-safe-action.dev/docs/usage-from-client/hooks/useaction See an example} | |||
*/ | |||
export const useAction = <const S extends Schema, const Data>( | |||
safeAction: SafeAction<S, Data>, | |||
safeAction: SafeAction<[S], Data>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now, I kept hook functionality as is, meaning that you can only use the hooks with actions that have exactly one input.
? { ...prev, ...buildValidationErrors(parsedInput.issues) } | ||
: prev; | ||
}, | ||
{} as Partial<Record<keyof InferArray<S>[number] | "_root", string[]>> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I simply merge all validation errors together, but this is a naive approach. Different arguments could have fields that overlap and it is hard to see from which argument a validation error came from.
Option 1
We could simply only return the validation errors of the first input that failed and avoid breaking changes.
type SafeActionResult<S extends Schema[], Data> = {
data?: Data;
serverError?: string;
validationErrors?: Partial<Record<keyof InferArray<S>[number] | "_root", string[]>>;
validationErrorsArgIndex?: number; // ADDED - needs a better name, but you yet the idea
}
Option 2
Other (more type safe, but breaking) option would be to do something like:
type ValidationErrors<S extends Schema> = Partial<Record<keyof Infer<S> | "_root", string[]>>
type SafeActionResult<S extends Schema[], Data> = {
data?: Data;
serverError?: string;
validationErrors: { [K in keyof S]: ValidationErrors<S[K]> };
}
So that the user can access validation errors like:
const input1Errors = result.validationErrors[0] // full type safety here
const input2Errors = result.validationErrors[1]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally, I would go for option 1 initially, considering this PR should not introduce any breaking change this way.
Option 2 would require a major version bump and users of the library would have to update their validation error handling logic through their codebase (however, this would be mostly done by a simple search & replace: result.validationErrors
> result.validationErrors[0]
)
what are your thoughts on this?
First of all, sorry for the huge delay in my response. I've been very busy this past month, and this, as you probably already know, is a pretty difficult feature to implement. I also wanted to lay the foundation to support this feature before giving you feedback, and I think the updated I'm still trying to find the best way to support Form Actions in the library, along with standard Server Actions, which are still very important (I'm using next-safe-action + React Hook Form combo in all of my apps). That said, since next-safe-action, in one way or another, will support them in v7, and since the major version bump comes from the I've read through your code, and even though everything works, I'm still considering whether this is the best way to implement this feature (not saying it isn't either). A different approach would be to provide an utility action wrapper function just for Form Actions, or maybe two, one for Server Form Actions and another one for Client Form Actions, along with a type safe wrapper for the If you still want to help me find the right way to support this functionality in the library, I'll gladly accept your contribution. Please let me know what you think about my thoughts and the different implementation options, and sorry again for the delay! |
Don't worry about the delay, I understand that life & work comes first : )
I didn't check the Let's say you have a nested resource: You would still need something like an variadic function (approach in this PR), unless you do something like: export const updateAnswer = formAction(
additionalArgsSchema,
formSchema,
async (additionalArgs, { newAnswer }) => {
console.log(
`Updating answer for question ${additionalArgs.questionId} of course ${additionalArgs.courseId} with: '${newAnswer}'`
);
return {
success: true,
};
}
); where the user has to bind the additional args: const action = updateAnswer.bind(null, { courseId: 1, questionId: 3 });
// pass action to form But not sure if it is better? |
Hey @bram209, first of all:
Thank you. So, I've thought a lot about the best way to implement Form Actions in next-safe-action. I've come to the conclusion that, in my opinion, the best approach is to have both variadic functions to support argument binding and a wrapper function for Form Actions used in Client Components, which require initial state as the first argument of the function. The wrapper (let's call it
What I don't really like is returning an array of Please let me know what you think about all of this, and since this will be a core feature of next-safe-action, the valuable thoughts of @varna, @benjick, @rwieruch and @theboxer would be greatly appreciated as well. Thank you! |
This PR is about supporting a variable number of arguments in Server Actions, but since that's strictly related to Form Actions, and we've already talked about it here in the comments above, here's an update on the Form Actions implementation: the React team "intends to fix some of the confusion and limitations of the useFormState hook", by introducing a new hook called |
@TheEdoRan sorry for a late response, I was going through it, stashed it and forgot to get back to it.... I don't really use React's form hooks or had a need to bind values to the server actions. I pretty much always go with I'd agree with your conclusion. Not 100% sure how the usage of the safe action will have to change for folks like me, who won't need to bind an extra arguments or use the form hooks. I'd hope it wouldn't change (much) :) |
Closed in favor of #97 |
not sure if this is the best approach DX wise, but having the ability to pass multiple arguments to actions let's you:
Pass additional arguments:
bind
method: https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations#passing-additional-argumentsUse the
useFormState
hook:To demonstrate this, I added two examples (see
form-with-bind
andform-with-state
)