easy way to wrap Field component as a child component? #1103
-
Sometimes, there are cases where I would like to wrap form.Field component inside of a wrapper / HOC. export default function TanstackFormPage() {
const renderCount = useRenderCount();
const [submittedFormValue, setSubmittedFormValue] = useState<object>({});
const form = useForm<SampleForm>({
defaultValues: {
name: "",
email: "",
message: "",
},
onSubmit: ({ value }) => {
setSubmittedFormValue(value);
},
});
return (
<div className="flex flex-col space-y-4 p-4 border-2 rounded-md">
<header className="text-lg font-semibold">{`Tanstack Form (rendered ${renderCount} times)`}</header>
<div className="flex flex-col space-y-2">
<span>Submitted Form Object:</span>
<pre>{JSON.stringify(submittedFormValue, null, 2)}</pre>
</div>
<form
className="flex flex-col space-y-2"
onSubmit={(e) => {
e.preventDefault();
form.handleSubmit();
}}
>
<form.Field name="name">
{(field) => (
<div className="flex flex-row space-x-4">
<input
value={field.state.value}
onChange={(e) => field.handleChange(e.target.value)}
/>
</div>
)}
</form.Field>
<form.Field name="email">
{(field) => (
<div className="flex flex-row space-x-4">
<input
value={field.state.value}
onChange={(e) => field.handleChange(e.target.value)}
/>
</div>
)}
</form.Field>
<form.Field name="message">
{(field) => (
<div className="flex flex-row space-x-4">
<input
value={field.state.value}
onChange={(e) => field.handleChange(e.target.value)}
/>
</div>
)}
</form.Field>
<button type="submit">Submit</button>
</form>
</div>
);
} but in order to split form.Field component, I have to go through something like this, type SampleForm = {
name: string;
email: string;
message: string;
}
export default function TanstackFormPage() {
const renderCount = useRenderCount();
const [submittedFormValue, setSubmittedFormValue] = useState<object>({});
const form = useForm({
defaultValues: {
name: "",
email: "",
message: "",
},
onSubmit: ({ value }) => {
setSubmittedFormValue(value);
},
});
return (
<div className="flex flex-col space-y-4 p-4 border-2 rounded-md">
<header className="text-lg font-semibold">{`Tanstack Form (rendered ${renderCount} times)`}</header>
<div className="flex flex-col space-y-2">
<span>Submitted Form Object:</span>
<pre>{JSON.stringify(submittedFormValue, null, 2)}</pre>
</div>
<form
className="flex flex-col space-y-2"
onSubmit={(e) => {
e.preventDefault();
form.handleSubmit();
}}
>
<ChildFormField<SampleForm>
api={form}
props={{
name: "name",
}}
/>
<ChildFormField<SampleForm>
api={form}
props={{
name: "email",
}}
/>
<ChildFormField<SampleForm>
api={form}
props={{
name: "message",
}}
/>
<button type="submit">Submit</button>
</form>
</div>
);
}
function ChildFormField<T>({
api,
props,
}: {
api: ReactFormExtendedApi<T, undefined>;
props: Omit<ComponentProps<typeof api.Field>, "children">;
}) {
const renderCount = useRenderCount();
return (
<api.Field {...props}>
{(field) => (
<div className="flex flex-row space-x-4">
<header>{`Child Form Field: ${props.name} (rendered ${renderCount} times)`}</header>
<input
value={field.state.value as string}
onChange={(e) =>
field.handleChange(
e.target.value as Parameters<typeof field.handleChange>[0]
)
}
/>
</div>
)}
</api.Field>
);
} Is there a easier workaround to split Field component into a part of other wrapper component? |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
I'm surprised this isn't talked about anywhere (here or in the docs). The "UI integration" example is nice and all, but how do we abstract this so we don't have to repeat all of the code everywhere? btw @heecheon92 nice job abstracting the Field component here |
Beta Was this translation helpful? Give feedback.
-
@heecheon92 @piotrpalek the short answer is because the functionality isn't finished yet. Theres an open PR #825 that will allow you to create re-usable components. But, it's not merged yet, theres a few internal types that need to be exposed, so unless you want to recreate them in your local code base you'll have to wait. The issue with abstracting the children like you have is the same issue I had here, you run into typing issues, and this only becomes more of an issue the more you have things like validators, listeners, and other functionality. But the maintainers are working on it, in-face it's the last checkmark on the v1.0 checklist. Hope this helps 🤟 |
Beta Was this translation helpful? Give feedback.
@heecheon92 @piotrpalek the short answer is because the functionality isn't finished yet.
Theres an open PR #825 that will allow you to create re-usable components. But, it's not merged yet, theres a few internal types that need to be exposed, so unless you want to recreate them in your local code base you'll have to wait.
The issue with abstracting the children like you have is the same issue I had here, you run into typing issues, and this only becomes more of an issue the more you have things like validators, listeners, and other functionality.
But the maintainers are working on it, in-face it's the last checkmark on the v1.0 checklist.
Hope this helps 🤟