Mingu
Mingu

Reputation: 121

React defaultValue for Select with Dynamically Generated Options

Use the defaultValue or value props on instead of setting selected on .

<select defaultValue="react">
 <option value="react">React</option>
 <option value="angular">Angular</option>
</select>

defaultValue would work with the above select tag. However, it does not seem to work with options generated by loop.

<select defaultValue={selectedOptionId}>
{option_id.map(id =>
  <option key={id} value={id}>{options[id].name}</option>
)}
</select>

Probably options not fully been set when defaultValue was declared?

I could do manual assigning in componentDidUpdate() and onChange event.

But my question is - Is there any cleaner(better) way to solve it?

Thanks.

Upvotes: 12

Views: 12830

Answers (5)

Alejandro
Alejandro

Reputation: 1

You can do something like this, React will complain because it uses value instead of selected for consistency across the form components, more information.

const {useState} = React;

const Example = ({title}) => {
    const [states, setStates] = useState({
      selected: "Florida",
      values: [{name: "Alabama", code: "AL"}, {name: "Hawai", code: "HW"},{name: "Florida", code: "FL"}]
     });
  
    return (
       <select
      className="w-full"
      onChange={(e: any) => {
        setStates({
          ...states,
          selected: e.target.value,
        });
      }}
    >
      {states.values.map((state) => (
        <option
          value={state.name}
          key={state.name}
          selected={state.name === states.selected}
        >
          {state.name}
        </option>
      ))}
    </select>
    );
};


ReactDOM.createRoot(
    document.getElementById("root")
).render(
    <Example title="Example using Hooks:" />
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>

Upvotes: 0

FrameMuse
FrameMuse

Reputation: 373

The best solution that I could find is to set key attribute the same as defaultValue so it will be a different element.

Aftermaths are not researched by me but I believe it should be okay.

Upvotes: 0

LasaleFamine
LasaleFamine

Reputation: 633

This is old, but since answer #1 is related to a controlled Select and the question seems to be related to uncontrolled Select I think is worth to leave some lines for future readers:

The problem is that for uncontrolled components React needs to know what are the options before the first render, since from that moment the defaultValue won't override the current value of the Select. This means that if you render the Select before the options it won't know what to select.

You can solve the problem avoiding the render before the options are available:

const RenderConditionally = ({ options, selected }) => options.length > 0 ? (
  <select defaultValue={selected}>
   {options.map(item => (
     <option key={item.id} value={item.value}>{item.label}</option>
   ))}
  </select>
) : null;

Or without ternary if you desire:

const RenderConditionally = ({ options, selected }) => {
  if (options.length === 0) {
    return null;
  }

  return (
    <select defaultValue={selected}>
      {options.map(item => (
        <option key={item.id} value={item.value}>{item.label}</option>
      ))}
    </select>
  );
};

Upvotes: 6

hellojebus
hellojebus

Reputation: 3583

For users running into this issue, you can get the desired functionality by using the value prop, instead of defaultValue, e.g.:

<select value={selectedOptionId}>
  {option_id.map(id =>
    <option key={id} value={id}>{options[id].name}</option>
  )}
</select>

Upvotes: 4

Alexandr Lazarev
Alexandr Lazarev

Reputation: 12862

Most probably you have something wrong with option_id and options arrays structure, or selectedOptionId variable. The way you build your select component is ok.

I've made a fiddle where this code works fine:

render: function() {
let option_id = [0, 1];
let options = [{name: 'a'}, {name: 'b'}];
let selectedOptionId = 1
return (
  <select defaultValue={selectedOptionId}>
    {option_id.map(id =>
    <option key={id} value={id}>{options[id].name}</option>
    )}
  </select>
)

}

Upvotes: 0

Related Questions