Pre-bound Field Components are not typesafe ? (React) #1240
-
I was trying to follow this guide on "Form Composition". import { createFormHook, createFormHookContexts } from "@tanstack/react-form";
function NumberField({ label }: { label: string }) {
const field = useFieldContext<number>();
return (
<label>
<div>{label}</div>
<input
value={field.state.value}
onChange={(e) => field.handleChange(e.target.valueAsNumber)}
/>
</label>
);
}
const { fieldContext, formContext, useFieldContext } = createFormHookContexts();
const { useAppForm } = createFormHook({
fieldContext,
formContext,
fieldComponents: {
NumberField: NumberField,
},
formComponents: {},
});
export const ExampleForm = () => {
const form = useAppForm({
defaultValues: {
email: "",
},
});
return (
<form.AppForm>
<form.AppField name="email">
{/* Should raise an error since NumberField expect a number and email is a string */}
{(field) => <field.NumberField label="Email" />}
</form.AppField>
</form.AppForm>
);
};
export { useAppForm }; |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
You can do this actually very easily yourself by adding a
|
Beta Was this translation helpful? Give feedback.
-
I think I found a more rigid way of doing this: Note the const TextField: FormField<string> = props => (
<input
// Just access `field` from props instead of context
value={props.field.state.value}
onChange={(e) => props.field.handleChange(e.currentTarget.value)}
/>
);
const Page: React.FC = props => {
const form = useAppForm({
defaultValues: { search: "", numeric: 0 },
onSubmit(props) {
console.debug(props);
},
});
return (
<form
onSubmit={(e) => {
e.preventDefault();
form.handleSubmit();
}}
>
<form.AppField
name="search"
children={(field) => <field.TextField field={field} />} // OK
/>
<form.AppField
name="numeric"
children={(field) => <field.TextField field={field} />} // Type error "Type 'number' is not assignable to type 'string'."
/>
<button type="submit">Submit</button>
</form>
);
};
/* My Types */
type FormFieldProps<T, P extends Record<string, unknown> = {}> = P & {
/** The `field` props comming from `<form.AppField children={field => ...} .../>`
* This ensures type safety */
field: FormFieldWithType<T>;
};
/** Type for a `@tanstack/react-form` component that uses the form state of type `T` and extra props `P` */
export type FormField<T, P extends Record<string, unknown> = {}> = React.FC<
FormFieldProps<T, P>
>;
// This is almost equal to `ReturnType<typeof useFieldContext>`.
// Setting `TName` to `string` makes typescript complain when assigning the `field={field}` property, so it's set to `any`
type FormFieldWithType<T> = FieldApi<any, any,
T, // Sets `TData` so that `typeof this.state.value === T`
any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any
>; |
Beta Was this translation helpful? Give feedback.
You can do this actually very easily yourself by adding a
value
field that goes unused: