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

Support ref in Select Component #2590

Open
Roanmh opened this issue Dec 14, 2023 · 6 comments
Open

Support ref in Select Component #2590

Roanmh opened this issue Dec 14, 2023 · 6 comments
Labels
Package: react/select Type: Enhancement Small enhancement to existing primitive/feature

Comments

@Roanmh
Copy link

Roanmh commented Dec 14, 2023

Feature request

Overview

The Select component does not allow a ref to be passed to the underlying html select element. This breaks react-hook-form's register() function; We are using remix-forms which uses register() to allow any custom component to be used in a form, as long as it can pass that ref to the underlying select. @jaimiz expressed the same problem in #1851 (comment)

The needed change is minimal; The Select component must accept a ref via React.forwardRef and pass the ref to BubbleSelect. BubbleSelect even supports the ref param, but it isn't used. Perhaps Select was intended to have this behavior originally?

We have already tried the solution in our project with patch-package and saw it work. I will create a PR with the change shortly. Is there any reason this wouldn't be accepted? Is there an alternative we haven't considered?

Examples in other libraries

N/A

Who does this impact? Who is this for?

  • Users of react-hook-form and remix-form
@Roanmh
Copy link
Author

Roanmh commented Dec 15, 2023

After working on this some more, I found a way to avoid passing the ref to the underlying select component. See the example below. I found #1045 (and related issues) so I understand that hiding the ref may be intentional. Feel free to close if so.

<Controller
  control={control}
  name="installation_type"
  render={({ field }) => {
    return (
      <SelectPrimitive.Root onValueChange={field.onChange} {...field}>
        <SelectPrimitive.Trigger>
          <SelectPrimitive.Value>
            {field.value}
          </SelectPrimitive.Value>
        </SelectPrimitive.Trigger>
        ...
      </SelectPrimitive.Root>
    )
  }}
></Controller>

Instead of expanding register() into the SelectPrimitive.Root's params, I used the Controller Component and it's render param. render offers a field param to it's callback which gives the needed params without relying on a ref. I then adapted specific field attributes to the SelectPrimitive.Root's params. e.g. onValueChange={field.onChange}. I also had to use field.value in the Value component for the trigger to be updated when an option was clicked.

@AlexKMarshall
Copy link

We also use Controller for this reason (and it has been popularised by the form implementation in ShadCN). But, you do lose a benefit of autofocusing a field that's in an error state. That only works if you use register and the field has been given a ref.

So, it would be great if these could be used uncontrolled.

@benoitgrelard benoitgrelard added Package: react/select Type: Enhancement Small enhancement to existing primitive/feature labels Mar 8, 2024
@benoitgrelard
Copy link
Contributor

This is something we need to do holistically throughout all of our primitives probably, so will need a bit more thought.
Related to #2659

@christian-reichart
Copy link

christian-reichart commented Oct 11, 2024

+1 on getting access to the select element via ref, having issues currently with Remix Validated Form. :(

@dantxal
Copy link

dantxal commented Nov 11, 2024

We also use Controller for this reason (and it has been popularised by the form implementation in ShadCN). But, you do lose a benefit of autofocusing a field that's in an error state. That only works if you use register and the field has been given a ref.

So, it would be great if these could be used uncontrolled.

I'm unsure wether the interface has changed since that, but I found that by using the ref available in SelectTrigger we can have auto-focus even when using Controller from react-hook-form.

@robotkutya
Copy link

robotkutya commented Dec 12, 2024

@dantxal Great tip to pass the ref to the <Select.Trigger /> component! It solves the auto-focus issue very nicely.

Here's how this looks like:

 <Controller
  // Makes it a bit more type safe, I prefer a typed form
  name={register("foo").name}
  control={control}
  render={({ field }) => {
    const { onChange, ref, ...rest } = field;

    return (
      <Select.Root onValueChange={onChange} {...rest}>
        <Select.Trigger ref={ref} />
        <Select.Content>
          {options.map(({ label, value }) => (
            // Make sure the values are unique!
            <Select.Item key={value} value={value}>
              {label}
            </Select.Item>
          ))}
        </Select.Content>
      </Select.Root>
    );
  }}
/>;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Package: react/select Type: Enhancement Small enhancement to existing primitive/feature
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants