primitives: [Select] Unable to clear value and return to `placeholder`
I’m playing with the new Select placeholder prop, and am wondering if the following is possible?
As an example, let’s say you have a series of 3 selects for an automobile and the placeholder values for each are “make”, “model”, and “year”. When you select a new “make” (after having already selected both a “make” and a “model”) and the underlying list for “model” is updated, the component still technically has a value based on the previous list’s selection, therefore the placeholder doesn’t render.
Is there a way to reset value when updating the list?
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 25
- Comments: 22 (1 by maintainers)
Commits related to this issue
- [Select] Use "" value to reset to placeholder Fixes #1569 — committed to radix-ui/primitives by benoitgrelard a year ago
- [Select] Use `""` `value` to reset to placeholder (#2174) * [Select] Use "" value to reset to placeholder Fixes #1569 * Update cypress/integration/Select.spec.ts Co-authored-by: Andy Hook <h... — committed to radix-ui/primitives by benoitgrelard a year ago
I no longer think this is an issue as I just learned something about React I didn’t know before.
Taking a look at the new beta docs, under the section, “Resetting all state when a prop changes”, it turns out that if you add a
keyprop to any component, React will reset all of the state inside of that component and all of its children when that key changes.Taking that advice, I made this sandbox where you can add/remove that
keyprop to see the effect.Btw, In those docs, the page, “You Might Not Need an Effect” has really changed the way I think about and build components.
Cheers
I’ve just ran into this myself, attempting to select a filter on a page search and then attempting to clear that filter. Unfortunate!
Edit: Here’s my solution
Select.RootSelect.RootAs suggested by cprecioso this forces the select to fully re-render. In my case I’m using
react-hook-formto control the value of the select- dumbed down example above.@andy-hook Same with me … When I select
valuethen clearvaluetoundefinedSelectPrimitivestill show previous valueBumping this, as its a pretty huge roadblock for us. A native “clear” functionality would be appreciated, but at a minimum, setting value to null/undefined needs to be possible to truly call this controlled.
In v2, you can just reset to
""and the placeholder will be displayedHey all, thank you for all your feedback on this. I have been working on a PR for this: #2174, let me know if you see any objections.
@dextermb , thanks for the example https://github.com/radix-ui/primitives/issues/1569#issuecomment-1434801848. Following resets controlled select input to its default value. Using react-hook-form.
The select component assumes it’s being uncontrolled when value is
undefined, which makes it effectively impossible to introduce an empty state in a controlled way. Ie. if you use some state that is initiallyundefinedand do some validation on callback toonValueChangewhich may prevent the outside state from changing, it won’t work: the select will update itself, even though value prop staysundefined.Solution for that would be to allow
nullas value which indicates an empty state in a controlled way.I believe it’s a bug. Workarounds proposed in this issue don’t work for me, as changing the key to rerender the entire component does not make sense in my case (and is not an idiomatic way to do things in react and impairs performance).
I tried introducing a dummy value and styling it like a placeholder, but it appears in the dropdown next to real items. If I use styles to hide it, the dropdown cannot position itself correctly.
AFAICT, the internal Radix
useControllableStatehook switches between controlled and uncontrolled throughvalue === undefined:https://github.com/radix-ui/primitives/blob/91763d2ed7d84e03e0e6f1307a7a29d9c7b04433/packages/react/use-controllable-state/src/useControllableState.tsx#L17-L19
So that means that once you’ve given the
valueany non-undefinedvalue, it is impossible to go back toundefined, as that will only cause the component to go to uncontrolled mode, and use the last value beforeundefinedas its current value.Moreover, you also can’t just pass
value={null}, because the placeholder check also uses avalue === undefinedcheck:https://github.com/radix-ui/primitives/blob/91763d2ed7d84e03e0e6f1307a7a29d9c7b04433/packages/react/select/src/Select.tsx#L337
The only workaround I can see is the aforementioned one with changing the
keyto force full re-creation of the component and its hooks lifecycle.If you don’t mind about it, just simply add
@ts-ignore. (Since you know what you’re doing)The problem is that null doesn’t display the placeholder, it might be kinda “hacky” to replace
<Select.Value />with the placeholder when value is null. That’s why the PR was created.I’ve also run into a similar issue, while using remix, of not being able to reset the placeholder after the form submission. I’m getting a formRef and resetting it after submission, but the Select doesn’t reset. I found that adding a key to the Form component and resetting this after form submission did the trick. So similar to these other solutions but at the Form component level.
Hi @andy-hook I’ll try to get a working example but it might take a day or two (sorry about that, I’m out of time right now). In the meantime, I can describe it better:
Looking at the
placeholdervalues, these only render ifvalueordefaultValueare not set.If you’ve already selected a “model”, but then
apiResponse.modelschanges and a different list is rendered, theSelectstill holds the previously selectedvalue(and thatvaluemight not correspond to an item in the new api response).So at that point in time, if the items have changed, I would expect
valueto reset so thatplaceholdercan render “model” again.Thinking about this further though, I think this could be solved on my end with the logic, “if the data set is empty, set the value as
undefined”.