I am working on an application where I am taking data(array of sectors) from firestore database and adding this array to select box options. My problem is that I can't properly display each item in a separate option, instead my entire array is displayed in one line in the select box option.
a screenshot of what my application looks like with the described problem.
Parts of the code responsible for getting an array from the database and displaying it in the select box options:
const [allSectors, setAllSectors] = useState([]);
useEffect(() => {
getSectors();
}, []);
const getSectors = async () => {
const data = await UserDataService.getAllSectorsData();
setAllSectors(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
};
const FormSelect = () => {
return allSectors.map((sector) => (
<option key={sector.id} value={sector.id}>
{sector.Manufacturing}
</option>
));
};
return (
<>
<Form onSubmit={handleSubmit}>
<Form.Group className="mb-3" controlId="formUserSector">
<InputGroup>
<InputGroup.Text id="formUserSector">Sectors: </InputGroup.Text>
<Form.Select size="sm" onChange={(e) => setSectors(e.target.value)}>
{FormSelect()}
</Form.Select>
</InputGroup>
</Form.Group>
</Form>
</>
)
Not sure which UI library you are using. The first thing I noticed is that you use <option> tag without surrounding it with <select> tag
<option key={sector.id} value={sector.id}>
{sector.Manufacturing}
</option>
I'm assuming you are trying to implement HTML <select> dropdown. I think if you use only tags without surrounding it with it will just return strings, which is what happening in your example.
Try changing your FormSelect function to something like this:
const FormSelect = () => {
return (
<select>
{allSectors.map((sector) => (
<option key={sector.id} value={sector.id}>
{item}
</option>
))}
</select>
);
};
EDIT 1
From your comments, I noticed that your sector.Manufacturing is an array, but it should be a string. When you put an array inside option like this <option>{array}</option> your get all the array values squashed into one <option> tag.
try also looping over sector.Manufacturing rather than allSector. Hard to solve without seeing the full code, but it might look something like this:
const FormSelect = () => {
return (
<select>
{allSectors.map((sector) => (
<option key={sector.id} value={sector.id}>
{sector.Manufacturing.map((item) => {
return item;
})}
</option>
))}
</select>
);
};
EDIT 2
Try this:
const FormSelect = () => {
return (
<select>
{allSectors.map((sector) =>
sector.Manufacturing.map((item, i) => {
return (
<option key={i} value={item}>
{item}
</option>
);
})
)}
</select>
);
};
Related
I feel like there is a simple solution to this but I've been stuck on it for a while.
This is what I did:
export default function App() {
const [searchTerm, setSearchTerm] = useState('');
return (
<div>
<div>
<input type="text" onChange={(e) => setSearchTerm(e.target.value)} />
</div>
<select>
<option defaultOption>Choose option</option>
{SelectOptions.filter(({ val }) => val.includes(searchTerm)).map(
({ val, id }) => {
return (
<option key={id} value={id}>
{val}
</option>
);
}
)}
</select>
</div>
);
}
sandbox: https://stackblitz.com/edit/react-qx5fym?file=src%2FApp.js
As you can see I have an input above my select and it works. If I search something, the select option shorten the list. However, I'm struggling on trying to get the input inside the select.
I want to be able to get this:
Exclude the styling that isn't the goal right now.
Anyone know how to achieve this?
I am attempting to create a dynamic form in which there are 2 text fields and one dropdown select. These fields can be added by clicking the "Add More.." button. The remove button removes a particular field set. After an npm start the code shows all elements normally, add, remove and input fields work as intended. However, the problem starts when the select is used. On selecting something, the app crashes and gives a white screen with the errors [tag:"formFields.map is not a function"] and [tag:"Consider adding an error boundary to your tree to customize error handling behavior."] I would appreciate any help that can resolve this. :)
P.S. I am learning react through building projects rather than the conventional method of sitting through hours of tutorials and figuring things out. I am grateful to any help that is offered to me.
import { useState } from "react";
function FoodPreferences(){
const [formFields, setFormFields] = useState([
{ name: '', age: '', food: '' }
])
const [foodState, setFoodState] = useState("dumpling");
const handleFormChange = (event, index) => {
let data = [...formFields];
data[index][event.target.name] = event.target.value;
setFormFields(data);
}
const handleSelectChange = (event, index) => {
const selectedFood = event.target.value
setFormFields(selectedFood)
}
const submit = (e) => {
e.preventDefault();
console.log(formFields, foodState)
}
const addFields = () => {
let object = {
name: '',
age: '',
food: ''
}
setFormFields([...formFields, object])
}
const removeFields = (index) => {
let data = [...formFields];
data.splice(index, 1)
setFormFields(data)
}
return (
<div className="App">
<form onSubmit={submit}>
{formFields.map((form, index) => {
return (
<div key={index}>
<input
name='name'
placeholder='Name'
onChange={event => handleFormChange(event, index)}
value={form.name}
/>
<input
name='age'
placeholder='Age'
onChange={event => handleFormChange(event, index)}
value={form.age}
/>
<select
className="custom-select"
value={form.food}
onChange={event => handleSelectChange(event,index)}
>
<option value="steak">Steak</option>
<option value="sandwich">Sandwich</option>
<option value="dumpling">Dumpling</option>
</select>
<button onClick={() => removeFields(index)}>Remove</button>
</div>
)
})}
</form>
<button onClick={addFields}>Add More..</button>
<br />
<button onClick={submit}>Submit</button>
</div>
);
}
export default FoodPreferences;
I have tried using the select component alone without looping it and it worked fine. The errors pop up when select component is placed under a map() for dynamic inputs (Adding or Removing Fields). I know that the error is either in the onChange part of my code for the select component or the handleSelectChange
import React, {useState} from 'react';
function FoodChoice() {
const \[foodState, setFoodState\] = useState("dumpling");
return (
<div className="container p-5">
<select
className="custom-select"
value={foodState}
onChange={(e) => {
const selectedFood = e.target.value;
setFoodState(selectedFood);
}}
>
<option value="steak">Steak</option>
<option value="sandwich">Sandwich</option>
<option value="dumpling">Dumpling</option>
</select>
{foodState}
</div>
);
}
export default FoodChoice;
I am adding a select component like code below. After adding an author, i have two authors in the array, but I am having two select with the code below. Sorry for being stupid first I just started learning React.
let authorNames = this.state.authors.map((author, index) => {
return (
<select
onChange={e => {
let { newBookData } = this.state;
newBookData.author_id = e.target.value;
this.setState({ newBookData });
}}
key={author.id}
className="form-control"
>
<option>Select an author ...</option>
<option key={author.id} value={author.id}>
{this.getAuthorName(author.id)}
</option>
</select>
);
});
and I add like this..
render(){
{authorNames}
}
then I have the view like this, having two selects. What i have done wrong?
You can see the result image from here
I guess what you want to build is a select input that has all the authors in the this.state.authors array as options. For that, this would be how I go about it.
let selectAuthorOptions = this.state.authors.map((author, index) => {
return (<option key={author.id} value={author.id}>
{this.getAuthorName(author.id)}
</option>)
})
selectAuthorOptions = [(<option key="default">Select an author ...</option>)].concat(selectAuthorOptions)
let selectAuthor = (<select onChange={e => {
let { newBookData } = this.state;
newBookData.author_id = e.target.value;
this.setState({ newBookData });
}}
className="form-control">
{selectAuthorOptions}
</select>
You'll need to render just 1 select then use the .map to create the options you want.
render(){
<select
onChange={e => {
let { newBookData } = this.state;
newBookData.author_id = e.target.value;
this.setState({ newBookData });
}}
key={author.id}
className="form-control"
>
<option>Select an author ...</option>
{this.state.authors.map((author, index) => {
<option key={author.id} value={author.id}>
{this.getAuthorName(author.id)}
</option>
})
</select>
}
I have a simple app for displaying data. The state changes from a select. However I want the default select option to be united kingdom. Currently the option defaults to Afghanistan as it's the first in the alphabet.
export default function CountrySelect() {
const [country, setCountry] = useState('GBR');
const countries = useFetch('https://covid19.mathdro.id/api/countries');
if (!countries) return null;
const countryArr = Object.entries(countries.countries).map(([key, value]) => {
return {
name: `${key}`,
code: `${value}`
};
});
return (
<div>
<h2>Showing: {country}</h2>
<select
onChange={(event) => setCountry(event.target.value)}
defaultValue={country}>
{countryArr.map((country) => (
<option value={country.code} key={country.name}>
{country.name}
</option>
))}
</select>
<Info url={`https://covid19.mathdro.id/api/countries/${country}`}></Info>
</div>
);
}
To clarify the country state is 'GBR' and data from 'GBR' or United Kingdom is displayed. It's the tag which I'm having the issue with.
If you can not use defaultValue, just add simple condions
const [countryState, setCountry] = useState('GBR');
{countryArr.map(country => {
if (country.name !== countryState) {
return (
<option
value={country.code}
label={country.name}
key={country.name}
>
{country.name}
</option>
);
} else {
return (
<option
value={country.code}
label={country.name}
key={country.name}
selected="selected"
>
{country.name}
</option>
);
}
})}
From the code, it seems to be HTML select and not react-select. There is no default value attribute to the default select. You can add the selected attribute to the required option here, like:
const [selectedCountry, setCountry] = useState({ code: <Countrycode of UK>});
// ..
// ..
<select
onChange={(event) => setCountry(event.target.value)}
{countryArr.map((country) => (
<option selected={selectedCountry.code === country.code} value={country.code} key={country.name}>
{country.name}
</option>
))}
</select>
Set the object property that you want to be the default option. Since you want the United Kingdom to be selected , set the defaultValue to the code that you get from the API.
Sandbox for reference : https://codesandbox.io/s/react-hooks-t1c2x
export default function CountrySelect() {
const [selectedCountry, setCountry] = useState({
code: "GB"
});
const countries = useFetch("https://covid19.mathdro.id/api/countries", {});
if (!countries) return null;
const countryArr = Object.entries(countries.countries).map(([key, value]) => {
return {
name: `${key}`,
code: `${value}`
};
});
return (
<div>
<select
onChange={event => setCountry(event.target.value)}
defaultValue={selectedCountry.code}
>
{countryArr.map(country => (
<option value={country.code} key={country.name}>
{country.name}
</option>
))}
</select>
</div>
);
}
I'm trying to render a select, and want to be able to do something like this to add options:
<Field component={RenderSelect} name="subjects" label="Subjects">
<option value="maths">Maths</option>
<option value="english">English</option>
</Field>
I have a createRenderer function:
const createRenderer = render => ({input, name, label, children}) => {
return (
<div key={name}>
<label htmlFor={name}>{label}</label>
{render(input, name, children)}
</div>
)
},
and my RenderSelect looks like this:
const RenderSelect = createRenderer((input, name, label, children) => {
return (
<select name={name} {...input}>
{children}
</select>
)
})
I was under the impression that I could just destructure the children prop off the Field like I do for input, name, label, etc, although this does not seem to work. When I run the code, no options appear in my select, and an inspection of the DOM verifies that there are no options. Any help would be much apprecited. Thanks in advance!
You just need to remove your label parameter
const RenderSelect = createRenderer((input, name, children) => {
return (
<select name={name} {...input}>
{children}
</select>
)
});