What is the intended approach for creating reusable fields? #637
-
Let's imagine I have a component that absolutely works only with <SomeSpecialField name="some.special" />
// or
<Field
name="some.special"
children={(field) => <SomeSpecialField field={field} />}
/> However now, in order not to lose all the type checking I have to write like this and it makes the code harder to read and is code copying: <Field
name="some.special"
children={(field) => (
<SomeSpecialField
name={field.name}
meta={field.meta}
value={field.state.value}
onBlur={field.handleBlur}
onChange={field.handleChange}
/>
)}
/> As much I understand there is no "silver bullet" at the moment so I have to choose between second variant or lose type checking. function SomeSpecialField<TName, TParentData extends DeepRecord<TName, SpecialType>>(props: {
field: FieldApi<TParentData, TName>
}): JSX.Element {} But I failed to implement |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 3 replies
-
We'll have docs on how to do this soon. FWIW it'll be fairly complex TS types that we expect folks to copy+paste into their codebase when it's ready |
Beta Was this translation helpful? Give feedback.
-
Any updates on the docs? I just started to go down this path and backed off once I realized how complex the types are. :-) |
Beta Was this translation helpful? Give feedback.
-
Interested in this as well, so I am following here. |
Beta Was this translation helpful? Give feedback.
-
This is the strategy im currently using (with some opinionated behavior): adapters.tsx export type FieldProp<T> = FieldApi<any, any, any, any, T>;
/** Common input props that are handled by form library */
export type FieldInputProps = "name" | "value" | "onBlur" | "onChange" | "onFocus" | "error";
//you could just not use any adapter at all, but I wanted a degree of separation between my components and the form library.
//this is not a hook technically, but it might contain hooks in the future so for now it should be treated as one.
export function useFieldAdapter<T>(field: FieldProp<T>): InputBridgeProps<T> {
const meta = field.state.meta;
//just take the first error. multiple errors feel overwhelming to user.
const error = meta.errors.find(err => typeof err === "string");
return {
name: field.name,
value: field.state.value,
meta: {
error: error,
touched: meta.isTouched,
dirty: meta.isDirty,
validating: meta.isValidating,
submitting: field.form.state.isSubmitting, //is this the right way to get form metadata?
},
events: {
onChange: (val) => field.handleChange(val),
onBlur: () => field.handleBlur(),
onFocus: () => undefined, //tanstack does not use onFocus, but other libraries do.
}
};
} checkbox-field.tsx (have to create a wrapper for each input type but this is good practice anyways...) import { FieldInputProps, FieldProp, useFieldAdapter } from "../adapters";
import { CheckboxInput, CheckboxInputProps } from "components/inputs/checkbox-input";
export type CheckboxFieldProps = (
& Omit<CheckboxInputProps, FieldInputProps>
& {
field: FieldProp<boolean>,
}
);
export default function CheckboxField(props: CheckboxFieldProps) {
const { field, ...passthrough } = props;
const { name, value, events, meta } = useFieldAdapter(field);
//this input is another non form connected wrapper
return <CheckboxInput
{...passthrough}
name={name}
value={value}
{...events}
error={meta.error}
disabled={meta.submitting || props.disabled}
required={props.required}
/>;
} then you can use it like this <Field
name="requireCertificate"
children={field => (
<CheckboxField
label="Require Certificate"
field={field}
/>
)}
/> |
Beta Was this translation helpful? Give feedback.
We'll have docs on how to do this soon.
FWIW it'll be fairly complex TS types that we expect folks to copy+paste into their codebase when it's ready