0

I'm trying to render a dynamic <select> element generated by React: the input-select component (see below), takes an array of objects and every object must contain the value for the <option> and its caption.

export default function InputSelect (props) {
  return (
    <label className="form__element">
      {props.caption}
      <select className="input-select">
        {props.items.map((item, i) => <option key={i} value={item.value}>{item.caption}</option>)}
      </select>
    </label>
  )
}

This becomes a problem when I try to pass an array of objects like this one:

[
  {code: "IT", name: "Italy"},
  {code: "US", name: "United States"},
]

where I have different keys which i cannot change since this data is retrieved from db.

How can i fix this?

3
  • 2
    Does <option key={item.code} value={item.code}>{item.value}</option> not work? Note: i've just used the property names from the objects in your sample array for purposes of illustration. Commented Jan 5, 2020 at 19:24
  • Yes it works, but i want to use this component with different datasets too Commented Jan 5, 2020 at 19:35
  • Then you'd need to map over your result array to create a new one with the correct property names before calling this component. Commented Jan 5, 2020 at 19:37

3 Answers 3

1

Use Array.prototype.map()

<InputSelect
  items={
   [
    {code: "IT", name: "Italy"},
    {code: "US", name: "United States"},
   ].map(country => ({value: country.code, caption: country.name}))
  }
/>
Sign up to request clarification or add additional context in comments.

Comments

1

Assuming you own the InputSelect component, you could have it take the caption and value keys from props, like this:

const { Fragment } = React

const countries = [
  {code: "IT", name: "Italy"},
  {code: "US", name: "United States"},
]

const languages = [
  {value: 'IT', caption: 'Italiano'},
  {value: 'DE', caption: 'Tedesco'}
]

const App = props => (
  <Fragment>
    <InputSelect caption="Country" items={countries} captionKey="name" valueKey="code" />
    <br />
    <InputSelect caption="Language" items={languages} />
  </Fragment>
)

function InputSelect ({ valueKey = 'value', captionKey = 'caption', ...props }) {
  return (
    <label className="form__element">
      {props.caption}
      <select className="input-select">
        {props.items.map((item) => <option key={item[valueKey]} value={item[valueKey]}>{item[captionKey]}</option>)}
      </select>
    </label>
  )
}

ReactDOM.render(<App />, document.body)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

This is one of only several options, which also include mapping over items to create a new array with the correct property keys, which could look like this:

const { Fragment } = React

const countries = [
  {code: "IT", name: "Italy"},
  {code: "US", name: "United States"},
]

const languages = [
  {value: 'IT', caption: 'Italiano'},
  {value: 'DE', caption: 'Tedesco'}
]

const App = props => (
  <Fragment>
    <InputSelect
      caption="Country"
      items={countries.map(
        ({ code, name }) => ({ value: code, caption: name })
      )}
    />
    <br />
    <InputSelect caption="Language" items={languages} />
  </Fragment>
)

function InputSelect (props) {
  return (
    <label className="form__element">
      {props.caption}
      <select className="input-select">
        {props.items.map((item) => <option key={item.value} value={item.value}>{item.caption}</option>)}
      </select>
    </label>
  )
}

ReactDOM.render(<App />, document.body)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

2 Comments

Also, you should use the value as the key on the options instead of the index like you mentioned in the comments.
That's often true, and if you check my comment on the question, I did that there. It's probably not important here, unless the author will be sorting, adding or removing items from these lists dynamically. I'll edit the snippets to show this, anyway.
0

You can pass a mapper object to your Select:

function App () {
    const apiData = [
      {code: "IT", name: "Italy"},
      {code: "US", name: "United States"},
    ]

     const mapper = {value: 'code', caption: 'name'}; 
         return(
           <InputSelect items={apiData} mapper={mapper} />
          )
   }



   const defaultMapper = {value: "value", caption: "label"}

   function InputSelect ({ items, caption, mapper = defaultMapper }) {
      const {value, caption} = mapper
      return (
        <label className="form__element">
          {caption}
          <select className="input-select">
            {items.map((item, i) => <option key={i} value={item[value]}>{item[caption]}</option>)}
          </select>
        </label>
      )
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.