Reputation: 33
I want to create 3 dropdowns such as country,state,city.Based on selection in country option it should populate the state and based on country and state selected option the city dropdown should populate itself in react js. Thanks in Advance
i want add more states here in the usa country
componentDidMount() {
this.setState({
countries: [
{
name: "Germany",
states: [
{
name: "A",
cities: ["Duesseldorf", "Leinfelden-Echterdingen", "Eschborn"]
}
]
},
{ name: "Spain", states: [{ name: "B", cities: ["Barcelona"] }] },
{ name: "USA", states: [{ name: "C", cities: ["Downers Grove"] }] },
{
name: "Mexico",
states: [{ name: ["D", "F", "H"], cities: ["Puebla"] }]
},
{
name: "India",
states: [
{ name: "E", cities: ["Delhi", "Kolkata", "Mumbai", "Bangalore"] }
]
}
]
});
}
changeCountry(event) {
this.setState({ selectedCountry: event.target.value });
this.setState({
states: this.state.countries.find(
(cntry) => cntry.name === event.target.value
).states
});
}
changeState(event) {
this.setState({ selectedState: event.target.value });
const stats = this.state.countries.find(
(cntry) => cntry.name === this.state.selectedCountry
).states;
this.setState({
cities: stats.find((stat) => stat.name === event.target.value).cities
});
}
i want to display more states and city in one country (3 dropdowns)
render() {
return (
<div id="container">
<h2>Cascading or Dependent Dropdown using React</h2>
<div>
<label>Country</label>
<select
placeholder="Country"
value={this.state.selectedCountry}
onChange={this.changeCountry}
>
<option>--Choose Country--</option>
{this.state.countries.map((e, key) => {
return <option key={key}>{e.name}</option>;
})}
</select>
</div>
<div>
<label>State</label>
<select
placeholder="State"
value={this.state.selectedState}
onChange={this.changeState}
>
<option>--Choose State--</option>
{this.state.states.map((e, key) => {
return <option key={key}>{e.name}</option>;
})}
</select>
</div>
<div>
<label>City</label>
<select placeholder="City">
<option>--Choose City--</option>
{this.state.cities.map((e, key) => {
return <option key={key}>{e}</option>;
})}
</select>
</div>
</div>
);
}
}
Upvotes: 2
Views: 21864
Reputation: 113
In user registration, we use the list of countries, states and cities & automatically fetch the data if the user selects the field.
import React, { useState, useEffect } from 'react';
import { Form, Button } from 'react-bootstrap';
import { Country, State, City } from 'country-state-city';
import axios from 'axios';
import { motion } from 'framer-motion';
import Swal from 'sweetalert2';
const Index= (props) => {
const [countries, setCountries] = useState([]);
const [states, setStates] = useState([]);
const [cities, setCities] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [selectedCountry, setSelectedCountry] = useState('');
const [selectedState, setSelectedState] = useState('');
const [selectedCity, setSelectedCity] = useState('');
useEffect(() => {
const getCountries = async () => {
try {
setIsLoading(true);
const result = await Country.getAllCountries();
let allCountries = [];
allCountries = result?.map(({ isoCode, name }) => ({
isoCode,
name
}));
const [{ isoCode: firstCountry } = {}] = allCountries;
setCountries(allCountries);
setSelectedCountry(firstCountry);
setIsLoading(false);
} catch (error) {
setCountries([]);
setIsLoading(false);
}
};
getCountries();
}, []);
useEffect(() => {
const getStates = async () => {
try {
const result = await State.getStatesOfCountry(selectedCountry);
let allStates = [];
allStates = result?.map(({ isoCode, name }) => ({
isoCode,
name
}));
const [{ isoCode: firstState = '' } = {}] = allStates;
setCities([]);
setSelectedCity('');
setStates(allStates);
setSelectedState(firstState);
} catch (error) {
setStates([]);
setCities([]);
setSelectedCity('');
}
};
getStates();
}, [selectedCountry]);
useEffect(() => {
const getCities = async () => {
try {
const result = await City.getCitiesOfState(
selectedCountry,
selectedState
);
let allCities = [];
allCities = result?.map(({ name }) => ({
name
}));
const [{ name: firstCity = '' } = {}] = allCities;
setCities(allCities);
setSelectedCity(firstCity);
} catch (error) {
setCities([]);
}
};
getCities();
}, [selectedState]);
const handleSubmit = async (event) => {
event.preventDefault();
try {
const { user } = props;
const updatedData = {
country: countries.find(
(country) => country.isoCode === selectedCountry
)?.name,
state:
states.find((state) => state.isoCode === selectedState)?.name || '', // or condition added because selectedState might come as undefined
city: selectedCity
};
await axios.post(`/register`, {
...user,
...updatedData
});
Swal.fire('Awesome!', "You're successfully registered!", 'success').then(
(result) => {
if (result.isConfirmed || result.isDismissed) {
props.resetUser();
props.history.push('/');
}
}
);
} catch (error) {
if (error.response) {
Swal.fire({
icon: 'error',
title: 'Oops...',
text: error.response.data
});
console.log('error', error.response.data);
}
}
};
return (
<Form className="input-form" onSubmit={handleSubmit}>
<motion.div
className="col-md-6 offset-md-3"
initial={{ x: '-100vw' }}
animate={{ x: 0 }}
transition={{ stiffness: 150 }}
>
<Form.Group controlId="country">
{isLoading && (
<p className="loading">Loading countries. Please wait...</p>
)}
<Form.Label>Country</Form.Label>
<Form.Control
as="select"
name="country"
value={selectedCountry}
onChange={(event) => setSelectedCountry(event.target.value)}
>
{countries.map(({ isoCode, name }) => (
<option value={isoCode} key={isoCode}>
{name}
</option>
))}
</Form.Control>
</Form.Group>
<Form.Group controlId="state">
<Form.Label>State</Form.Label>
<Form.Control
as="select"
name="state"
value={selectedState}
onChange={(event) => setSelectedState(event.target.value)}
>
{states.length > 0 ? (
states.map(({ isoCode, name }) => (
<option value={isoCode} key={isoCode}>
{name}
</option>
))
) : (
<option value="" key="">
No state found
</option>
)}
</Form.Control>
</Form.Group>
<Form.Group controlId="city">
<Form.Label>City</Form.Label>
<Form.Control
as="select"
name="city"
value={selectedCity}
onChange={(event) => setSelectedCity(event.target.value)}
>
{cities.length > 0 ? (
cities.map(({ name }) => (
<option value={name} key={name}>
{name}
</option>
))
) : (
<option value="">No cities found</option>
)}
</Form.Control>
</Form.Group>
<Button variant="primary" type="submit">
Register
</Button>
</motion.div>
</Form>
);
};
export default Index;
Upvotes: 0
Reputation: 1452
Your solution is in a good direction. Though you can simplify it. Here's an example on how you can do it using React Hooks.
https://codesandbox.io/s/sweet-monad-kzp3p?file=/src/App.js
We start off by creation 3 states:
const [selectedCountry, setSelectedCountry] = React.useState();
const [selectedState, setSelectedState] = React.useState();
const [selectedCity, setSelectedCity] = React.useState();
const availableState = data.countries.find((c) => c.name === selectedCountry);
const availableCities = availableState?.states?.find(
(s) => s.name === selectedState
);
Normally their values will be undefined
. Every time you make a selection in the dropdown, we can update them. When updated, we can use the data
to find the corresponding states
(in availableState
). And if a state has been selected, we can use that property to find the cities
for this state.
The ?.states
is a null check. So if the availableState
has not been set yet, we won't try to call any functions on it.
This way we can use the same properties for both rendering and finding the correct data. Removing a lot of update complexity.
The render would look like this:
return (
<div id="container">
<h2>Cascading or Dependent Dropdown using React</h2>
<div>
<label>Country</label>
<select
placeholder="Country"
value={selectedCountry}
onChange={(e) => setSelectedCountry(e.target.value)}
>
<option>--Choose Country--</option>
{data.countries.map((value, key) => {
return (
<option value={value.name} key={key}>
{value.name}
</option>
);
})}
</select>
</div>
<div>
<label>State</label>
<select
placeholder="State"
value={selectedState}
onChange={(e) => setSelectedState(e.target.value)}
>
<option>--Choose State--</option>
{availableState?.states.map((e, key) => {
return (
<option value={e.name} key={key}>
{e.name}
</option>
);
})}
</select>
</div>
<div>
<label>City</label>
<select
placeholder="City"
value={selectedCity}
onChange={(e) => setSelectedCity(e.target.value)}
>
<option>--Choose City--</option>
{availableCities?.cities.map((e, key) => {
return (
<option value={e.name} key={key}>
{e}
</option>
);
})}
</select>
</div>
</div>
);
Upvotes: 2