I'm working on a React App. I have multiple select tags and different options to display and I wrote a function that receives the options and returns the whole select tag. I call this function multiple times from the render() method. I wanted to reuse the code. For some reasons the select options and not showing.
My code is something like that:
DisplayMenu = (id, options) => {
let menu = <div className="model"> Model
<select id={id}>
{
this.options.map( (item, index) => {
<option value={index}>{item}</option>
})
}
</select>
</div>
return menu;
}
render(){
return (
<div id="main">
// other <p> elements here
<Collapsible trigger="Actors">
{this.DisplayMenu("FirstActor", this.props.Details["FirstActor"])}
{this.props.Details["OtherActors"].map ( ( item, index) =>
this.DisplayMenu("OtherActor" + index, item)
)}
</Collapsible>
//oher <button> tags here
</div>
)
}
The functions displays the select tag but options are empty. My arrays are good, I checked the values. I assumed that the cause may be the z-index value for the tag, I tried to chage it but again nothing is displayed. Also in the browser console this is displayed:
<select id="FristActor model "></select> ==$0
Any ideas will help. Thank you.
You're not returning from your map function
<select id={id}>
{
this.options.map( (item, index) => {
return <option value={index}>{item}</option>
})
}
</select>
Sorry i'm not able to comment due to less reputation.
Updating Kumar's answer if you not want to write return.
<select id={id}>
{
this.options.map( (item, index) => (
<option value={index}>{item}</option>
))
}
</select>
Thanks.
Related
My API is returning below result.
I have below code to display in list.
const [result,setResult] = useState([]);
const fetchStatus = async ()=>{
await httpClient.get( config.resourceServerUrl+"/certificates/status").then(res=>{
setResult(res.data);
setActive(true);
alert(result);
})
and I am displaying list like below.
<div className="col-md-4">
<label htmlFor="status">Status</label>
<select
name="requestStatus"
style={{ display: 'block' }}>
<option value="" selected >
Please Select the Status
</option>
{
active && result.map((sts:any)=>{
<option key="" value="">
{sts}
</option>
})
}
</select>
though there is no error but it is not displaying anything.
Its because you've got {} around your JSX, should be ()
active && result.map((sts:any) => (
<option>{sts}</option>
))
or you can do
active && result.map((sts:any) => {
return (
<option>{sts}</option>
)
})
I don't believe you await httpClient.get. The .then will be called when the get completes.
you are setting the dropdown to each object in the returned array. You want to set it to the value of the request_status key: sts.request_status
set the key attribute for elements you create using map
{
active && result.map((sts:any)=>{
{sts.request_status}
})
}
For the function of saving search terms, search keywords are stored in localStorage and shown through the datalist.
But this shows too much data.
So I want to show up to 5 rows of data.
To limit the number of search terms that are stored.
I don't think I'm using the autocomplete function properly, so I need another way.
<datalist id="searchHistory">
{
keywords.map((item, index) => {
return <option key={index} value={item.text}/>
})
}
</datalist>
You can use Array.slice() as shown below to only render first 5 items.
<datalist id="searchHistory">
{
keywords.slice(0, 5).map((item, index) => {
return <option key={index} value={item.text}/>
})
}
</datalist>
I am trying to render a simple drop down with numbers from 1 to 100. I create an object with an array as its only element. I populate each array element with a value and then I try to map the object to generate the drop down items. This is the code I am executing:
renderPercent = () => {
let obj = {
array: []
};
for (var l=0;l<100;l++){
obj.array[l] = l+1;
}
console.log("obj: ", obj);
let optionItems = obj.array.map((item) =>
<option key={item}>{item}</option>
);
return (
<div>
<div className="row">
<div className="col-9 text-left">
<label>Select the percent to transfer</label>
</div>
<div className="col-2 text-left">
<select>
{optionItems}
</select>
</div>
<div className="col-1 text-left">
<label> </label>
</div>
</div>
</div>
)
}
I modified the optionItems map function as suggested. This is what now is generated when this screen is rendered:
As you can see, the map function is now populating my drop down. However, the dropdown goes below the end of the device. Is this just something that is a bug when viewing in Chrome with developer tools? And the other thing I noticed is that the numbers in the dropdown are a larger font than the reset of the page. Before I click on the dropdown arrow, the value 1 shows in the drowdown and that is the correct size, but as soon as I expand the drop down, the numbers seem much larger. Why would that be?
Again, thanks to the stackoverflow community!
obj is an object, it doesn't have a map function. Whereas obj.array is a list that have a map function.
The following code
let optionItems = obj.map((item) =>
<option key={item.array}>{item.array}</option>
);
should be
let optionItems = obj.array.map((item) =>
<option key={item}>{item}</option>
);
You are trying to map on an object, not an array, modify your code as so:
obj.map((item) =>
should be
obj["array"].map((item) =>
You can see that in your console.log. It says { array: [....
You can do this.
{Array.from(Array(100), (e, i) => {
return <option value={i+1}>{i+1}</option>
}
)}
"i" will contain a value from 0 to 100 that's why I make it "i+1"
You are doing .map on object but not on array. Try with . notation
let optionItems = obj.array.map((item) =>
<option key={item.array}>{item.array}</option>
);
complete code
renderPercent = () => {
let obj = {
array: []
};
for (var l=0;l<100;l++){
obj.array[l] = l+1;
}
return (
<div>
<select>
{obj.array.length > 0 && obj.array.map((item) =>
<option key={item.array}>{item.array}</option>
)}
</select>
</div>
)
}
Try some like this:
var percentArray = [];
for (var l=0;l<100;l++){
percentArray.push(l);
}
let optionItems= percentArray.map(x => `<option key=${x}>${x}</option>`);
...
return (
<div>
<select>
{optionItems}
</select>
</div>
)
Could someone please explain how to fix this error
Warning: flattenChildren(...): Encountered two children with the same
key
I have replicated my code below, but for some reason CodePen is not showing the error.
var FilterOptions = React.createClass({
changeOption: function(type, e) {
var val = e.target.value;
this.props.changeOption(val, type);
},
render: function() {
return (
<div className="filter-options">
<div className="filter-option">
<select id="product" name="Product" value={this.props.product} onChange={this.changeOption.bind(this, 'product')}>
<option value=''>Product</option>
{this.props.productOptions.map(function(option) {
return (<option key={option} value={option}>{option}</option>)
})}
</select>
</div>
</div>
);
}
});
Codepen
As a secondary question, I am pretty sure my reset is supposed to reset the values of the select boxes but this is also not working and just resetting the rendered results - not sure if this is related to the first problem?
Any help much appreciated
It is not a good idea to use the index as the key. A key is the only thing React uses to identify DOM elements. What happens if you push an item to the list or remove something in the middle? If the key is same as before React assumes that the DOM element represents the same component as before. But that is no longer true. From: https://medium.com/#robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318
It is much better to use a unique string from each item you are mapping over as the key. Something like <option key={value.id}> or if a key does not exist, create a unique identifier by doing something like <option key={value.name + value.description}>.
Adding the index as value fixed this. Thanks #azium for your sugegstion.
<select id="product" name="Product" value={this.props.product} onChange={this.changeOption.bind(this, 'product')}>
<option value=''>Product</option>
{this.props.productOptions.map(function(option, value) {
return (<option key={value} value={option}>{option}</option>)
})}
</select>
I'm a big fan of using key by combining index with some constant value rather than using key={value.name + value.description}:
key={'some-constant-value'+index}
This is because I can pass the key knowingly which compoent is it for. For eg. <ComponentA key={'compoent-a-'+i} />. Also, I follow this approach is because simple html convention matches like we give id="my-some-of-the-id" or something.
So, even if you want to use name and description as the key, you may use like this rather:
key={'some-constant-'+value.name+'-'+value.description}
This is just an opinion. Though, I follow html convention when writing props value.
actually you need to specify to each children a unique key,so for that you need to create another key,for example if you are getting data from the database so for that create a new column for example (id) and then add value of that column to your div or what matter you are looping on as a key
var FilterOptions = React.createClass({
changeOption: function(type, e) {
var val = e.target.value;
this.props.changeOption(val, type);
},
render: function() {
return (
<div className="filter-options">
<div className="filter-option">
<select id="product" name="Product" value={this.props.product} onChange={this.changeOption.bind(this, 'product')}>
<option value=''>Product</option>
{this.props.productOptions && this.props.productOptions.map(function(option) {
return (<option key={option.id} value={option}>{option}</option>)
})}
</select>
</div>
</div>
);
}
});
i hope this help anyone in the future.
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.
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>
);
};
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>
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>
)
}
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.