I have a React component that has multiple <select>
(not react-select) elements. Only one <select>
is rendered based on one of the props:
class SelectGroup extends Component { ... render () { let activeSelect switch (this.props.foo) { case 'select1': activeSelect = <select id="select1"> <option value="1a" selected>1 A</option> <option value="1b">1 B</option> <option value="1c">1 C</option> </select> break case 'select2': activeSelect = <select id="select2"> <option value="2a" selected>2 A</option> <option value="2b">2 B</option> <option value="2c">2 C</option> </select> break ... } return activeSelect } ... }
(For our purposes, assume that SelectGroup
is nested inside another component, which changes the prop foo
when a button is clicked.)
What I want is for the first option for each <select>
to be selected by default whenever we change which <select>
is being rendered (whenever the prop foo
is changed). As you can see, I gave the first option of each <select>
the selected
attribute which, according to this documentation, should make it the default option. However, that’s not the behavior I’m seeing. Here’s what happens:
select1
is being displayed. I choose option1b
from the dropdown.- I click the button to switch to
select2
. - I expect for
2a
to be selected because it has theselected
attribute. However,2b
is selected instead. - I select
2c
from the dropdown. - I click the button to switch back to
select1
. - I expect for
1a
to be selected. However,1c
is selected instead.
It seems to be completely ignoring the selected
attribute and instead it looks like it’s selecting the option from the list that corresponds to whichever option was selected before changing which <select>
is rendered.
Oddly, if I remove the selected
attribute from all <select>
elements except select1
, then select1
will behave as expected: when I switch to select1
, it will always display option 1a
regardless of what was selected before, but the option displayed by select2
will continue to depend on whatever was chosen before switching to it. So it’s as if selected
only works if it is only used once, but I don’t understand why that is, since it is only being applied to one option per <select>
.
How can I get the first option to be selected whenever I switch between which <select>
is being rendered?
Advertisement
Answer
Try adding a unique key
to each select:
switch (this.props.foo) { case 'select1': activeSelect = <select key="select1" id="select1"> <option value="1a" selected>1 A</option> <option value="1b">1 B</option> <option value="1c">1 C</option> </select> break case 'select2': activeSelect = <select key="select2" id="select2"> <option value="2a" selected>2 A</option> <option value="2b">2 B</option> <option value="2c">2 C</option> </select> break ...
This will force a complete rerender of the select. What is happening is most likely when you switch from one to the other, react’s VDOM kicks in and it modifies the existing select rather than replaces it. Due to (quite weird and archaic) DOM behaviors, the selected value of the old select is retained.
By using a unique key
it will force React to completely rerender the whole node, throwing away any selected value state in memory in the process.
What you are doing is a bit weird btw — there are almost certainly better ways but I don’t know enough about your use case. You probably want to instead conditionally render them inline, which means in the internal representation of the component held by React, they are 2 separate nodes:
return <> {this.props.foo === "select1" && <select id="select1"> <option value="1a" selected>1 A</option> <option value="1b">1 B</option> <option value="1c">1 C</option> </select> } {this.props.foo === "select2" && <select id="select1"> <option value="1a" selected>1 A</option> <option value="1b">1 B</option> <option value="1c">1 C</option> </select> } </>
You might think “how is this any different”. Well, the inactive ones will render false (which renders nothing). But that it renders “false” means that node is represented separately as that value and not stuffed into one, which is why reacts vdom is thinking it’s the exact same node.