Skip to content
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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add prop types of custom components #121

Open
bvangraafeiland opened this issue Dec 7, 2022 · 4 comments
Open

Add prop types of custom components #121

bvangraafeiland opened this issue Dec 7, 2022 · 4 comments

Comments

@bvangraafeiland
Copy link
Contributor

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 a size 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.

@danielweinmann
Copy link
Contributor

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?

@bvangraafeiland
Copy link
Contributor Author

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.

@danielweinmann
Copy link
Contributor

I love the idea of the hooks, @bvangraafeiland! Can you create separate issues for each hook you'd like to exist? Thank you!

@ReptoxX
Copy link
Contributor

ReptoxX commented Jul 20, 2023

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/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants