Repository: heroui-inc/heroui (TypeScript)

Issue:
[BUG] - Select value not reset after unselect an item
### NextUI Version

2.4.1

### Describe the bug

In an uncontrolled form, after select and then unselect an item, reading form data in`onSubmit` still gets the selected value for this field.

### Your Example Website or App

```
const onSubmit = useCallback((e: FormEvent) => {
  e.preventDefault()
  const formData = new FormData(e.target as HTMLFormElement)
  alert(JSON.stringify(Object.fromEntries(formData), null, 2))
}, [])

return (
  <form onSubmit={onSubmit}>
    <Select name="account_filter" label="Account filter" size="sm">
      <SelectItem key="verified">From verified users</SelectItem>
      <SelectItem key="follows">From accounts you follow</SelectItem>
    </Select>
  </form>
)
```

### Steps to Reproduce the Bug or Issue

1. Select an item in `Select`
2. Unselect the item
3. Submit the form

You can see the process in my video attachment.

### Expected behavior

I'd expect to get empty value for this Select field

### Screenshots or Videos

https://github.com/nextui-org/nextui/assets/321947/2551f829-ac5c-4679-954e-343d9e5d99dd



### Operating System Version

macOS

### Browser

Chrome


Discussion:
<p><a href="https://linear.app/nextui-inc/issue/ENG-941/[bug]-select-value-not-reset-after-unselect-an-item">ENG-941 [BUG] - Select value not reset after unselect an item</a></p>



Patch:
diff --git a/.changeset/thirty-timers-press.md b/.changeset/thirty-timers-press.md
new file mode 100644
index 0000000000..64f0c14683
--- /dev/null
+++ b/.changeset/thirty-timers-press.md
@@ -0,0 +1,5 @@
+---
+"@nextui-org/select": patch
+---
+
+set empty string instead of undefined for unsetting value (#3156)
diff --git a/packages/components/select/src/hidden-select.tsx b/packages/components/select/src/hidden-select.tsx
index 4523c3968a..d7c74ef43a 100644
--- a/packages/components/select/src/hidden-select.tsx
+++ b/packages/components/select/src/hidden-select.tsx
@@ -113,7 +113,7 @@ export function useHiddenSelect<T>(
       value:
         selectionMode === "multiple"
           ? [...state.selectedKeys].map((k) => String(k))
-          : [...state.selectedKeys][0],
+          : [...state.selectedKeys][0] ?? "",
       multiple: selectionMode === "multiple",
       onChange: (e: React.ChangeEvent<HTMLSelectElement>) => {
         state.setSelectedKeys(e.target.value);


Test patch:
diff --git a/packages/components/select/__tests__/select.test.tsx b/packages/components/select/__tests__/select.test.tsx
index 75431dccbb..280ae91f74 100644
--- a/packages/components/select/__tests__/select.test.tsx
+++ b/packages/components/select/__tests__/select.test.tsx
@@ -547,6 +547,75 @@ describe("Select", () => {
 
     expect(select).toHaveTextContent("Select an animal");
   });
+
+  it("should unset form value", async () => {
+    const logSpy = jest.spyOn(console, "log");
+
+    const user = userEvent.setup();
+
+    const wrapper = render(
+      <form
+        className="w-full max-w-xs items-end flex flex-col gap-4"
+        onSubmit={(e) => {
+          e.preventDefault();
+          const formData = new FormData(e.target as HTMLFormElement);
+
+          /* eslint-disable no-console */
+          console.log(JSON.stringify(Object.fromEntries(formData)));
+        }}
+      >
+        <Select data-testid="select" label="test select" name="select" size="sm">
+          <SelectItem key="foo">foo</SelectItem>
+          <SelectItem key="bar">bar</SelectItem>
+        </Select>
+        <button data-testid="submit-button" type="submit">
+          Submit
+        </button>
+      </form>,
+    );
+
+    const select = wrapper.getByTestId("select");
+
+    expect(select).not.toBeNull();
+
+    await act(async () => {
+      await user.click(select);
+    });
+
+    let listbox = wrapper.getByRole("listbox");
+
+    expect(listbox).toBeTruthy();
+
+    let listboxItems = wrapper.getAllByRole("option");
+
+    expect(listboxItems.length).toBe(2);
+
+    await act(async () => {
+      await user.click(listboxItems[1]);
+    });
+
+    let submitButton = wrapper.getByTestId("submit-button");
+
+    await act(async () => {
+      await user.click(submitButton);
+    });
+
+    expect(logSpy).toHaveBeenCalledWith(JSON.stringify({select: "bar"}));
+
+    await act(async () => {
+      await user.click(select);
+    });
+
+    await act(async () => {
+      await user.click(listboxItems[1]);
+    });
+
+    await act(async () => {
+      await user.click(submitButton);
+    });
+
+    expect(logSpy).toHaveBeenCalledWith(JSON.stringify({select: ""}));
+  });
 });
 
 describe("Select with React Hook Form", () => {