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

Invalid Select value in the form entries #234

Open
rago4 opened this issue Jan 13, 2024 · 3 comments
Open

Invalid Select value in the form entries #234

rago4 opened this issue Jan 13, 2024 · 3 comments
Labels
bug Something isn't working

Comments

@rago4
Copy link

rago4 commented Jan 13, 2024

Hey! First up, thank you for your hard work on Radix UI, it's a great library and I love working with it. Now, let's get straight to the point. Recently I've been playing around with Select component and I found a strange behaviour. Basically, the value seems to be invalid once retrieved by using form.entries() after submitting <form>. In the documentation, I saw that every example of <Select.Root> uses defaultValue instead of value but I need it to be dynamic as I'm setting the initial value by using search parameters. Also, I could simply use value from React state and call it a day, but I need it in a Remix action (server side), so it won't do the job here 😅

Here's the code to reproduction:

import { useState } from 'react';
import { Button, Select } from '@radix-ui/themes';

const colors = [
  { value: 'red', label: 'Color red' },
  { value: 'green', label: 'Color green' },
  { value: 'blue', label: 'Color blue' },
];

export default function App() {
  const [color, setColor] = useState('');

  return (
    <div>
      <form
        onSubmit={(event) => {
          event.preventDefault();
          const form = new FormData(event.currentTarget);
          // without state change: the value is "red", should be ""
          // after state change the value is assigned correctly
          // after clearing selection with "Clear" button and submitting: the value is undefined, should be ""
          console.log(Object.fromEntries(form.entries()));
        }}
      >
        <Select.Root name="color" value={color} onValueChange={setColor}>
          <Select.Trigger placeholder="Select a color" />
          <Select.Content>
            <Select.Group>
              {colors.map((color) => (
                <Select.Item key={color.value} value={color.value}>
                  {color.label}
                </Select.Item>
              ))}
            </Select.Group>
          </Select.Content>
        </Select.Root>
        <Button type="submit">Submit</Button>
        <Button type="button" variant="outline" onClick={() => setColor('')}>
          Clear
        </Button>
      </form>
    </div>
  );
}

And here's the link to the live demo I've prepared: https://stackblitz.com/edit/vitejs-vite-w3x9aw?file=src%2FApp.tsx&terminal=dev

Thank you once again and sorry if I missed something and this turns out to be expected behaviour 😅

@vladmoroz vladmoroz added the bug Something isn't working label Jan 16, 2024
@onurhan1337
Copy link

onurhan1337 commented Feb 3, 2024

Hi, @rago4,

The issue you're experiencing is due to how the Radix UI Select component handles its value. The Select component doesn't behave like a traditional HTML select element, and it doesn't directly interact with the form it's in. This means that when you submit the form, the value of the Select component isn't automatically included in the form data.

To work around this, you can create a hidden input field in your form that holds the value of the Select component. When the form is submitted, the value of the hidden input field will be included in the form data.

Here's how you can modify your code to include a hidden input field:

import { useState, useCallback } from 'react';
import { Button, Select } from '@radix-ui/themes';
import { v4 as uuidv4 } from 'uuid';

const colors = [
  { value: 'red', label: 'Color red' },
  { value: 'green', label: 'Color green' },
  { value: 'blue', label: 'Color blue' },
];

export default function App() {
  const [color, setColor] = useState('');
  const [key, setKey] = useState(uuidv4());

  const reset = useCallback(() => {
    setColor('');
    setKey(uuidv4()); // Change key to reset Select component
  }, []);

  const handleSubmit = useCallback((event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    const form = new FormData(event.currentTarget);
    console.log(Object.fromEntries(form.entries()));
  }, []);

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <Select.Root key={key} name="color" onValueChange={setColor}>
          <Select.Trigger placeholder="Select a color" />
          <Select.Content>
            <Select.Group>
              {colors.map((color) => (
                <Select.Item key={color.value} value={color.value}>
                  {color.label}
                </Select.Item>
              ))}
            </Select.Group>
          </Select.Content>
        </Select.Root>
        <input type="hidden" name="color" value={color} />
        <Button type="submit">Submit</Button>
        <Button type="button" variant="outline" onClick={reset}>
          Clear
        </Button>
      </form>
    </div>
  );
}

In this code, I've added a hidden input field with the name color and the value set to the current color state. When the form is submitted, the value of this hidden input field will be included in the form data.

@onurhan1337
Copy link

Actually, Using a UUID for the key in this specific case could be considered overengineering. The key just needs to change to trigger a re-render of the component, it doesn't need to be globally unique or follow the UUID format.

Using Math.random() is perfectly fine in this case, as the likelihood of generating the same number twice in a row is extremely low.

So, you can stick with your original approach: I've gave an example with uuid.

@vladmoroz
Copy link
Contributor

The Select component should natively work with forms out of the box—we'll take a look

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants