I have code like the following. When a button is clicked, a value in a form is updated. There is a select whose options come from the form. This involves an API request to get the new options, and so it is done asynchronously using TRPC.
const schema = {
name: z.string(),
}
const defaultValues = {
name: "",
}
function App() {
const form = useForm({
resolver: zodResolver(schema),
defaultValues,
mode: "onSubmit"
});
function onClick() {
form.setValue("name", "A");
}
return (
<form>
<Button onClick={onClick}>Click</Button>
<SelectInput form={form} />
</form>
)
}
function SelectInput({form}) {
const name = form.watch("name");
const result = trpc.items.useQuery({ name }); // When this resolves, it will contain "A"
const items = result.data || [];
return (
<Select>
<SelectTrigger>
<SelectValue placeholder="Select" />
</SelectTrigger>
<SelectContent>
{items.map((item) => (
<SelectItem value={item} key={item}>
{item}
</SelectItem>
))}
</SelectContent>
</Select>
)
}
This leads to the following:
Click button to set form name to "A"
Select re-renders, triggering an asynchronous TRPC query
While the query is resolving, there are no select items. This causes this effect to run, which sets the value of the form back to "".
TRPC query finishes, populating the items. However, the form value has been reset by Radix-select.
Expected behavior
I want to be able to set the value of the select input to something that is not a current select item. As a workaround currently, I can return early if the name isn't filled in, but that is not ideal.
Reproducible example
This stackblitz is a simplified example of the above, using useTimeout instead of TRPC to set the options.
This example uses Shadcn-ui, which uses Radix select. The select has options of "A", "B", and "C". When the button is clicked to set the value to "A", the select updates properly.
"D" is not in the list of items. When the button is clicked to set the value to "D", a timeout fires to update the list of options. If you look at the console, "Changed" is logged with an empty string. This is because the BubbleSelectchange event fires.
If the "Set to D" button is clicked again, it works because "D" is part of the options list.
When the select value is changed to "D", the form value is properly updated for a moment. Then, this effect runs, and sets the value back to an empty string. I'm not sure what needs to change here to let this work.
Bug report
Current Behavior
I have code like the following. When a button is clicked, a value in a form is updated. There is a select whose options come from the form. This involves an API request to get the new options, and so it is done asynchronously using TRPC.
This leads to the following:
""
.Expected behavior
I want to be able to set the value of the select input to something that is not a current select item. As a workaround currently, I can return early if the name isn't filled in, but that is not ideal.
Reproducible example
This stackblitz is a simplified example of the above, using
useTimeout
instead of TRPC to set the options.https://codesandbox.io/p/devbox/trusting-hill-wqjlvq
This example uses Shadcn-ui, which uses Radix select. The select has options of "A", "B", and "C". When the button is clicked to set the value to "A", the select updates properly.
"D" is not in the list of items. When the button is clicked to set the value to "D", a timeout fires to update the list of options. If you look at the console, "Changed" is logged with an empty string. This is because the
BubbleSelect
change event fires.If the "Set to D" button is clicked again, it works because "D" is part of the options list.
Suggested solution
This is caused by this code:
https://github.com/radix-ui/primitives/blob/b32a93318cdfce383c2eec095710d35ffbd33a1c/packages/react/select/src/Select.tsx#L1556-L1577
When the select value is changed to "D", the form value is properly updated for a moment. Then, this effect runs, and sets the value back to an empty string. I'm not sure what needs to change here to let this work.
Additional context
n/a
Your environment