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鈥檒l occasionally send you account related emails.
Already on GitHub? Sign in to your account
Controlled mode 馃憖 #10
Comments
Thanks! 馃槉 The new I think some of your use cases can be solved with useValue(). BUT! Not nothing actually prevents you from using controlled inputs all the way with React Zorm. It does not care. It only requires that the The "馃洃 No controlled inputs" bullet point it the readme might be a bit miss leading 馃 It just means you are not forced to use controlled inputs but definitely can if you want to. |
Very good point! I guess in this way -- one might imagine a stateful lib which uses react-zorm for its underlying primitives? My interest is piqued. I might have to experiment with this... We're in a rut with Formik at the moment -- the overriding problems are performance and type safety, but we've been holding off alternatives achieving the perf via uncontrolled. I've got pictured in my head an approach which leverages Perhaps another challenge that this could solve is that of server errors. Could field-level server errors (which by their nature are controlled), be reconciled with the client side zod errors? Hmm...I'm gonna need to go away and experiment. |
Certainly possible. You might want to consider setting setupListeners option to false because it gives you total control on when to run the Zod validation which is probably nice when building something on top of it.
Currently React Zorm has no opinions on this. So far I've just passed totally independent error object from the server and rendered it on to the form.
It should be possible via /**
* Form handler response type
*/
interface FormHandlerResponse {
errors?: Record<string, string | undefined>;
}
/**
* Generates Zod .superRefine() handlers based on the server response
*/
function refineServerError(res: FormHandlerResponse | undefined) {
return (_value: unknown, ctx: z.RefinementCtx) => {
// We do not care about the value since value was validated on the
// server already. We just need to add the error here.
if (!res) {
// Not submitted yet
return;
}
// Match zod path to server errors. This supports only one level errors atm.
const errorMessage = res.errors?.[ctx.path[0]];
if (errorMessage) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Server error: " + errorMessage,
});
}
};
}
export default function MyForm() {
// useActionData() is Remix.run hook for accessing form handler responses.
const formResponse = useActionData<FormHandlerResponse>();
const schema = useMemo(() => {
const refine = refineServerError(formResponse);
return z.object({
username: z.string().superRefine(refine),
email: z.string().superRefine(refine),
});
}, [formResponse]);
const zo = useZorm("contact", schema);
// ...
} This would be bit cleaner if Zod would allow adding issues to nested field from the top-level object like this: FormSchema.superRefine((value, ctx) =>{
ctx.addIssueToPath(["username"], {
code: z.ZodIssueCode.custom,
message: "Server error: " + errorMessage,
});
}); So we could get away with single I'd might be interested adding support for this directly in React Zorm if it becomes possible. I imagine it could look something like this: const zo = useZorm("contact", FormSchema, {
refineErrors(addError) {
if (response.errors.username) {
addError(["username"], {
code: z.ZodIssueCode.custom,
message: response.errors.username,
});
}
},
}); |
Hmm, actually I think react-zorm could have another source (other than zod) for errors which could be used for server-side errors which would be read by the error chain. Something like: const zo = useZorm("contact", FormSchema, {
serverErrors: [
{
path: ["user", "userName"],
message: "Username is already in use",
}
],
}); where you would of course define the |
@esamattis Thanks for your detailed responses, I really appreciate the time spent thinking about this. I didn't know about superRefine, so that's very helpful for me. Zod is a gift that keeps on giving.
Yeh, we have built something similar on top of Formik, and could probably do it here as well. I think the challenge is though is that its better if they exist in state owned by the form lib, since you sometimes want behaviour such as "typing in the field that has a server error clears it". That implies that the state of those errors ought to be owned by the lib. In our Formik solution that's possible because you can Perhaps one generic approach might be to be able to register some callback which the dev can define. The return object of that would be an enhanced/merged error object.
Although TBH I think your recipe that leans on Zod is very valid and perhaps in keeping with the purposeful coupling to Zod -- I cant see a problem with it other than that the schema will probably need to be brought into the component scope, which hurts a little bit the composability of those schemas outside a react context. Minor niggle though! |
@adam-thomas-privitar Thanks for the ideas! Just implemented a The documentation is here: Here's a full Remix example: Async field validation with React Query This is now available in v0.3.0.
The
Yeah. I intentionally try to keep this lib fairly low-level to keep it small and easy to maintain. Less features, less bugs. But I'd like to find the "correct" building blocks so this lib could be used to build something more high level. This lib actually has only two components:
|
Closing as not actionable. |
I love the idea of this library. I always thought Zod and Forms feels like a match made in heaven and this library is a great demonstration of an ergonomic API that leverages Zod.
However, whilst uncontrolled mode leads to good perf (and is partly why react-hooks-form got popular), you lose a lot of stuff (I think? I'm wanting to be wrong!). Things like:
For really complex form flows, its difficult without being in controlled mode.
But what about perf? Well...React 18 to the rescue (?). Ive started a conversation here on Formik which outlines the idea of utilizing
useTransition
to get the best of both worlds: jaredpalmer/formik#3532The text was updated successfully, but these errors were encountered: