Open bvangraafeiland opened 1 year ago
I've been thinking a lot about a good solution for that. The size
example came up to me yesterday when I created an example of using Remix Forms with Chakra UI.
In the example, I simply omitted the size prop :P But I think we need to have a better way.
The only solution I thought of so far is to add a generic to Form
that will define the types of each component, with the default being what we have now. But I'd love to see if anybody else has ideas :)
@diogob @gustavoguichard, what do you think?
The render function of the Field
component could be made generic, so in this case:
<Form inputComponent={MyInputComponent}>
<Field name='name'>
{({ Input }) => (
<Input />
)}
</Field>
</Form>
The Input
render prop of Field
would have the props of MyInputComponent
.
That being said, I personally think it would be a better to expose a useField
to use within custom components (as well as useFormState
like mentioned in #37). This makes it easier to implement custom fields that will be default in all forms, rather than relying on the renderField
prop. This becomes quite verbose if you want to preserve the layout of checkboxes. With a hook, the required asterisk for example could be done somewhat like this:
const MyLabel = (props) => {
const { required, label } = useField();
return <label {...props}>{label}{required && '*'}</Label>;
}
Something similar could be done for indicators on input fields.
I love the idea of the hooks, @bvangraafeiland! Can you create separate issues for each hook you'd like to exist? Thank you!
I'm not sure if this might be helpful, but i'm using a sort of "polymorphic" component in my projects (similar to mantine). With that you can use a component like <MenuItem />
and add a as
prop to it and pass another element in and get correct types of that element. It even works with custom Elements and gives you every prop of the custom element.
<MenuItem as='input' />
// Gives you types for the input like value, autocomplete or whatever + the props of MenuItem.
type AsProp<C extends React.ElementType> = {
as?: C;
asElement?: C;
};
type PropsToOmit<C extends React.ElementType, P> = keyof (AsProp<C> & P);
export type PolymorphicComponentProp<C extends React.ElementType, Props = {}> = React.PropsWithChildren<Props & AsProp<C>> &
Omit<React.ComponentPropsWithoutRef<C>, PropsToOmit<C, Props>>;
export type PolymorphicRef<C extends React.ElementType> = React.ComponentPropsWithRef<C>['ref'];
export type PolymorphicComponentPropWithRef<C extends React.ElementType, Props = {}> = PolymorphicComponentProp<C, Props> & {
ref?: PolymorphicRef<C>;
};
Big thanks to this article: https://blog.logrocket.com/build-strongly-typed-polymorphic-components-react-typescript/
When using the field children, you can pass some props to the Label/Input etc. However, the prop types of the components are
JSX.IntrinsicElements['input']
for example, so if you have a custom input component that takes asize
prop, you end up with a typescript error.In reality the props are being passed through, so it will work if you ignore the type error, but it would be nice if the prop types could be merged somehow.