How to set the initial value of a select in React? - reactjs

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>
);
}

Related

Display items from array in select box react

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>
);
};

How to have second dropdown menu values be dependent on value of first dropdown menu in React render function

In the render function of a React component, I have the following code
<select name="env" id="env">
<option value="Dev">Dev</option>
<option value="Staging">Staging</option>
<option value="Prod">Prod</option>
</select>
<select name="region" id="region">
<option value="option1">op1</option>
<option value="option2">op2</option>
<option value="option3">op3</option>
<option value="option4">op4</option>
</select>
If Dev option is selected in first dropdown, there should only be op1 choice available in the 2nd dropdown. Similarly if Staging is selected, then op1, op2 and op3 are the available choices. If Prod is selected, then all 4 choices are available. How can I achieve this in React?
You should render different region options by env.
const regionsByEnv = {
Dev: ["option1"],
Staging: ["option1", "option2", "option3"],
Prod: ["option1, option2, option3, option4"],
};
const YourComponent = () => {
const [env, setEnv] = useState("");
const regionOptions = useMemo(() => {
if (!env) {
return regionsByEnv.Prod.map((option) => (
<option value={option} key={option}>
{option}
</option>
));
}
return regionsByEnv[env].map((option) => (
<option value={option} key={option}>
{option}
</option>
));
}, [env]);
return (
<>
<select
value={env}
onChange={(e) => setEnv(e.target.value)}
name="env"
id="env"
>
<option value="Dev">Dev</option>
<option value="Staging">Staging</option>
<option value="Prod">Prod</option>
</select>
<select name="region" id="region">
{regionOptions}
</select>
</>
);
};
You should filter option for select region base on selected option of select env. So you can create a state for handling value of select env
export default function App() {
const [select1, setSelect1] = useState("Dev");
const filterOption2 = allOption2.filter((i) => {
return (
(select1 === "Dev" && i.value === "option1") ||
(select1 === "Staging" && i.value !== "option4") ||
select1 === "Prod"
);
});
return (
<div className="App">
<select name="env" id="env" onChange={(e) => setSelect1(e.target.value)}>
<option value="Dev">Dev</option>
<option value="Staging">Staging</option>
<option value="Prod">Prod</option>
</select>
<select name="region" id="region">
{filterOption2.map((op) => (
<option key={select1 + op.value} value={op.value}>
{op.name}
</option>
))}
</select>
</div>
);
}
const allOption2 = [
{
name: "op1",
value: "option1"
},
{
name: "op2",
value: "option2"
},
{
name: "op3",
value: "option3"
},
{
name: "op4",
value: "option4"
}
];
You can check in my codesandbox

remove option from second select if already selected in reactjs

I have two select input to select years (years,years_1) , when i change years to 2020 i want to remove 2020 from years_1 but when i change years to 2021 then in years_1 2020 must add back and 2021 will remove (or disable also work).
useEffect(() => {
const currentYear = new Date().getFullYear();
const yearsArray = [];
for (let i = currentYear; i > currentYear - 3; i--) {
yearsArray.push(i.toString());
yearsArray.sort();
}
setYears(yearsArray);
setYears_1(yearsArray);
setCommonYear(yearsArray);
}, []);
there setYears,setYears_1,setCommonYear are useStates to store year array.
if (years !== null && years_1 !== null) {
if (years.includes(value)) {
setYears_1((prevState) => prevState.filter((item) => item !== value));
} else {
setYears_1(commonYear);
}
}
this is what i have tried
<select
name="year"
value={turnoverModels?.year}
onChange={handleChangeTurnoverYear}
>
<option value="NA" selected>
NA
</option>
{years.map((item) => (
<option key={item} value={item}>
{item}
</option>
))}
</select>
<select
name="year"
value={turnoverModels?.year}
onChange={handleChangeTurnoverYear}
>
<option value="NA" selected>
NA
</option>
{years_1.map((item) => (
<option key={item} value={item}>
{item}
</option>
))}
</select>
Added state to store the values of both the select dropdowns. Then we can use the useEffect() hook to find when the value of first select changes and update the years_1 array.
Demo : Codesandbox
import { useEffect, useState } from "react";
import "./styles.css";
export default function App() {
const [years, setYears] = useState([]);
const [years_1, setYears_1] = useState([]);
const [select1, setSelect1] = useState(null);
const [select2, setSelect2] = useState(null);
useEffect(() => {
const yearsArray = getYears();
setYears(yearsArray);
setYears_1(yearsArray);
}, []);
// When the first year changes, update the years_1 array
useEffect(() => {
if (!select1) return;
const filtered = getYears().filter((y) => y !== select1);
setYears_1(filtered);
}, [select1, years_1]);
const getYears = () => {
const currentYear = new Date().getFullYear();
const yearsArray = [];
for (let i = currentYear; i > currentYear - 3; i--) {
yearsArray.push(i.toString());
yearsArray.sort();
}
return yearsArray;
};
return (
<div className="App">
<select
name="year"
value={select1}
onChange={(e) => setSelect1(e.target.value)}
>
<option value="NA" selected>
NA
</option>
{years.map((item) => (
<option key={item} value={item}>
{item}
</option>
))}
</select>
<select
name="year"
value={select2}
onChange={(e) => setSelect2(e.target.value)}
>
<option value="NA" selected>
NA
</option>
{years_1.map((item) => (
<option key={item} value={item}>
{item}
</option>
))}
</select>
</div>
);
}
setYears_1 again when select in years array:
Example: https://codesandbox.io/s/winter-sky-hbm8ec?file=/src/App.js
import { useEffect, useState } from "react";
import "./styles.css";
export default function App() {
const [years, setYears] = useState([]);
const [years_1, setYears_1] = useState([]);
const [select1, setSelect1] = useState(null);
const [select2, setSelect2] = useState(null);
useEffect(() => {
const yearsArray = getYears();
setYears(yearsArray);
setYears_1(yearsArray);
}, []);
useEffect(() => {
if (!select1) return;
const filtered = getYears().filter((y) => y !== select1);
setYears_1(filtered);
}, [select1, years_1]);
const getYears = () => {
const currentYear = new Date().getFullYear();
const yearsArray = [];
for (let i = currentYear; i > currentYear - 3; i--) {
yearsArray.push(i.toString());
yearsArray.sort();
}
return yearsArray;
};
return (
<div className="App">
<select
name="year"
value={select1}
onChange={(e) => setSelect1(e.target.value)}
>
<option value="NA" selected>
NA
</option>
{years.map((item) => (
<option key={item} value={item}>
{item}
</option>
))}
</select>
<select
name="year"
value={select2}
onChange={(e) => setSelect2(e.target.value)}
>
<option value="NA" selected>
NA
</option>
{years_1.map((item) => (
<option key={item} value={item}>
{item}
</option>
))}
</select>
</div>
);
}

Set default value of dropdown in React

I have an array like [ {code: 'in', name: 'India' }, ...] for all countries and rendered a dropdown list with all countries as option using map. I want to set India as default value when the component renders but I always get Argentina as default option because it is first on the array.
Please suggest me a way to achieve it. I tried finding answers but those are not working or may be I'm doing something wrong. Here is the code required :
export default function NavBar({ setUserSearch, country, setCountry }) {
const handleCountryChange = (e) => {
setCountry({code: e.target.value, name: e.target.innerText})
}
return (
<select className="country_dropdown mx-3 py-1 px-1 text-dark" onChange = {handleCountryChange}>
{countries.map(country => {
return <option value = {country.code} key = {country.code}>{country.name}
</option>
}
)
}
</select>
)
}
option has an attribute called "selected", which is a boolean setting.
So you can do this -
<select className="country_dropdown mx-3 py-1 px-1 text-dark" onChange = {handleClick}>
{countries.map(country => {
return <option value={country.code} key={country.code} selected={country.code === 'in'}>
{country.name}
</option>
})}
</select>
Let me know if this works out for you.

Characters filter by property value in React.js

I'm a trying to build an app based on Rick and Morty api.
I want to filter characters by species.
I've created separate components for each species value
For example:
Human.js
export const Human = ({ match }) => {
const { species } = match.params;
const [characterFilters, setCharacterFilters] = useState([]);
useEffect(() => {
try {
fetch('https://rickandmortyapi.com/api/character?species=human')
.then((res) => res.json())
.then((res) => setCharacterFilters(res))
.catch((err) => console.log(err));
} catch (e) {
console.log(e);
}
}, []);
const { name, image } = characterFilters;
return (
<div>
<h2>Name: {name}</h2>
<div>
<img alt={name} src={image}></img>
</div>
</div>
);
};
In the characters file I've set the conditions based on select propery chosen
Characters.js
export const Characters = () => {
const [characters, setCharacters] = useState();
useEffect(() => {
try {
fetch(CHARACTERS_PAGE_URL)
.then((res) => res.json())
.then(({ results }) => {
if (results && Array.isArray(results)) {
setCharacters(results);
}
})
.catch((err) => console.log(err));
} catch (e) {
console.log(e);
}
}, []);
const handleSpeciesChange = (e) => {
console.log("[e.target.value]", e.target.value);
switch (e.target.value) {
case "human":
return <Human />;
break;
case "alien":
return <Alien />;
break;
case "unknown":
return <Unknown />;
break;
default:
return;
}
};
const handleStatusChange = (e) => {
switch (e.target.value) {
case "alive":
return <Human />;
break;
case "dead":
return <Alien />;
break;
case "unknown":
return <Unknown />;
break;
default:
return;
}
};
if (!characters) {
return <Loading />;
}
return (
<div className="p-4 font-mono text-green-500 ">
<div className="flex flex-row">
<div className="m-4 ">
<label>Species</label>
<select name="species" id="species" onChange={handleSpeciesChange}>
<option value="all">all</option>
<option value="human">human</option>
<option value="alien">alien</option>
<option value="unknown">unknown</option>
</select>
</div>
<div className="m-4">
<label>Status</label>
<select name="status" id="status" onChange={handleStatusChange}>
<option value="all">all</option>
<option value="alive">alive</option>
<option value="dead">dead</option>
<option value="unknown">unknown</option>
</select>
</div>
<div className="m-4">
<label>Gender</label>
<select name="gender" id="gender">
<option value="all">all</option>
<option value="female">female</option>
<option value="male">male</option>
<option value="genderless">genderless</option>
<option value="unknown">unknown</option>
</select>
</div>
</div>
<h1 className="text-4xl">Characters</h1>
<Pagination data={characters} pageLimit={5} />
<div className="grid grid-flow-col grid-cols-5 grid-rows-4 gap-4">
{characters.map((character) => (
<div key={character.id}>
<Character character={character} />
</div>
))}
</div>
</div>
);
};
But I'm not getting filtered characters.
I'm new to React.
Please help me to understand, what am I doing wrong here?
Is there a possibility to created a common filter component that I would be able to apply for different properties filters?
You cannot return jsx from a callback
Make filters get stored and only render these values from the array:
export const Characters = () => {
const [characters, setCharacters] = useState();
const [ speciesFilter, setSpeciesFilter ] = useState('');
const [ status, setStatus] = useState('');
useEffect(() => {
try {
fetch(CHARACTERS_PAGE_URL)
.then((res) => res.json())
.then(({ results }) => {
if (results && Array.isArray(results)) {
setCharacters(results);
}
})
.catch((err) => console.log(err));
} catch (e) {
console.log(e);
}
}, []);
const handleSpeciesChange = (e) => {
console.log("[e.target.value]", e.target.value);
setSpeciesFilter(e.target.value)
};
const handleStatusChange = (e) => {
setStatus(e.target.value);
};
if (!characters) {
return <Loading />;
}
let filteredCharacters = characters;
if (speciesFilter) {
filteredCharacters = filteredCharacters.filter(c => c.species === speciesFilter);
}
if (status ) {
filteredCharacters = filteredCharacters.filter(c => c.status === status);
}
return (
<div className="p-4 font-mono text-green-500 ">
<div className="flex flex-row">
<div className="m-4 ">
<label>Species</label>
<select name="species" id="species" onChange={handleSpeciesChange}>
<option value="all">all</option>
<option value="human">human</option>
<option value="alien">alien</option>
<option value="unknown">unknown</option>
</select>
</div>
<div className="m-4">
<label>Status</label>
<select name="status" id="status" onChange={handleStatusChange}>
<option value="all">all</option>
<option value="alive">alive</option>
<option value="dead">dead</option>
<option value="unknown">unknown</option>
</select>
</div>
<div className="m-4">
<label>Gender</label>
<select name="gender" id="gender">
<option value="all">all</option>
<option value="female">female</option>
<option value="male">male</option>
<option value="genderless">genderless</option>
<option value="unknown">unknown</option>
</select>
</div>
</div>
<h1 className="text-4xl">Characters</h1>
<Pagination data={characters} pageLimit={5} />
<div className="grid grid-flow-col grid-cols-5 grid-rows-4 gap-4">
{filteredCharacters.map((character) => (
<div key={character.id}>
<Character character={character} />
</div>
))}
</div>
</div>
);
};
The above example uses a filter which can be improved
what am I doing wrong here?
You are returning JSX from the callback, for example,
onChange={handleStatusChange}
Here you pass the function which will be passed the event for you to handle, the problem is, returning a component from this function doesn't do anything, it doesn't tell React to render the component you returned.
Instead, you can store the value from there in state, and do something accordingly, for example, filter the array using that value
Is there a possibility to created a common filter component that I
would be able to apply for different properties filters?
That can be done, by passing the characters to that component and the filter values
export const FilterCharacters = ({ characters, status, specie }) => {
let filteredCharacters = characters;
if (specie ) {
filteredCharacters = filteredCharacters.filter(c => c.species === specie);
}
if (status ) {
filteredCharacters = filteredCharacters.filter(c => c.status === status);
}
return filteredCharacters.map((character) => (
<div key={character.id}>
<Character character={character} />
</div>
))
}
As for your Human and specific species components, it calls the API for filtered values, but I assume you already got all values initially, so you could only filter from there, if that's not the case, you could make Human more generic by passing the species and status, and querying the API with that values

Resources