I want to populate state data as per selected country. This is working fine.
But I have two use this condition multiple times in a single page. How can I do this?
Issue Screenshot attached:-

Sandbox Url:- https://codesandbox.io/s/country-state-sibling-issue-rdphoc?file=/src/App.js
My Code:-
import React, { useState, useEffect } from "react";
import "./styles.css";
import { TextField, MenuItem } from "@mui/material";
export default function App() {
const body = [
{
state_ID: 1,
state: "Delhi",
country_ID: 1,
country_name: "India"
},
{
state_ID: 2,
state: "Mumbai",
country_ID: 1,
country_name: "India"
},
{
state_ID: 3,
state: "Calgary",
country_ID: 2,
country_name: "Canada"
},
{
state_ID: 4,
state: "Toronto",
country_ID: 2,
country_name: "Canada"
}
];
const [country, setCountry] = useState([]);
const [state, setState] = useState([]);
const [selectedCountry, setSelectedCountry] = useState("");
useEffect(() => {
const uniqValues = [
...new Map(body.map((item) => [item["country_name"], item])).values()
];
setCountry(uniqValues);
setState(body);
}, []);
useEffect(() => {
const newStates = body.filter(
({ country_name }) => country_name === selectedCountry
);
console.log(selectedCountry, newStates);
setState(newStates);
}, [selectedCountry]);
useEffect(() => {}, [body, country]);
return (
<>
<TextField
className="ruleContainer"
select
name="Country"
label="Country"
variant="outlined"
size="small"
onChange={(event) => setSelectedCountry(event.target.value)}
>
{country
? country.map((opt) => (
<MenuItem
key={opt.country_name}
value={opt.country_name}
onChange={(value) => setSelectedCountry(value)}
>
{opt.country_name}
</MenuItem>
))
: ""}
</TextField>
<TextField
className="ruleContainer"
select
name="state"
label="State"
variant="outlined"
size="small"
value=""
>
{state
? state.map((opt) => (
<MenuItem key={opt.state} value={opt.state}>
{opt.state}
</MenuItem>
))
: ""}
</TextField>
<hr />
<TextField
className="ruleContainer"
select
name="Country"
label="Country"
variant="outlined"
size="small"
onChange={(event) => setSelectedCountry(event.target.value)}
>
{country
? country.map((opt) => (
<MenuItem
key={opt.country_name}
value={opt.country_name}
onChange={(value) => setSelectedCountry(value)}
>
{opt.country_name}
</MenuItem>
))
: ""}
</TextField>
<TextField
className="ruleContainer"
select
name="state"
label="State"
variant="outlined"
size="small"
value=""
>
{state
? state.map((opt) => (
<MenuItem key={opt.state} value={opt.state}>
{opt.state}
</MenuItem>
))
: ""}
</TextField>
<hr />
</>
);
}
Thanks for your efforts!
To achieve multiple same forms with country and state dependent. You need to create the custom component with those form fields and maintain the state in that. So it will not affect the main component state.

You can find a working sample at https://codesandbox.io/s/country-state-sibling-issue-forked-9wcmi9?file=/src/App.js
CountryStateFormItems component
import { useState, useEffect } from "react";
import { TextField, MenuItem, Box } from "@mui/material";
const body = [
{
state_ID: 1,
state: "Delhi",
country_ID: 1,
country_name: "India"
},
{
state_ID: 2,
state: "Mumbai",
country_ID: 1,
country_name: "India"
},
{
state_ID: 3,
state: "Calgary",
country_ID: 2,
country_name: "Canada"
},
{
state_ID: 4,
state: "Toronto",
country_ID: 2,
country_name: "Canada"
}
];
export default function CountryStateFormItems(props) {
const [country, setCountry] = useState([]);
const [state, setState] = useState([]);
const [selectedCountry, setSelectedCountry] = useState(props.form.country);
const [selectedState, setSelectedState] = useState(props.form.state);
useEffect(() => {
const uniqValues = [
...new Map(body.map((item) => [item["country_name"], item])).values()
];
setCountry(uniqValues);
}, []);
useEffect(() => {
const newStates = body.filter(
({ country_name }) => country_name === selectedCountry
);
setState(newStates);
}, [selectedCountry]);
useEffect(() => {
props.updateForm(props.index, selectedCountry, selectedState);
}, [selectedState]);
return (
<>
<Box display="flex">
<TextField
style={{ flex: 1 }}
className="ruleContainer"
select
name="Country"
label="Country"
variant="outlined"
size="small"
value={selectedCountry}
onChange={(event) => setSelectedCountry(event.target.value)}
>
{country
? country.map((opt) => (
<MenuItem
key={opt.country_name}
value={opt.country_name}
onChange={(value) => setSelectedCountry(value)}
>
{opt.country_name}
</MenuItem>
))
: ""}
</TextField>
<TextField
style={{ flex: 1 }}
className="ruleContainer"
select
name="state"
label="State"
variant="outlined"
size="small"
value={selectedState}
onChange={(event) => setSelectedState(event.target.value)}
>
{state
? state.map((opt) => (
<MenuItem
key={opt.state}
value={opt.state}
onChange={(value) => setSelectedState(value)}
>
{opt.state}
</MenuItem>
))
: ""}
</TextField>
</Box>
<hr />
</>
);
}
App component
import React, { useState, useCallback } from "react";
import "./styles.css";
import { Button } from "@mui/material";
import CountryStateFormItems from "./CountryStateFormItems";
export default function App() {
const [forms, setForms] = useState([]);
const addForm = () => {
const existingForms = [...forms];
existingForms.push({
country: "",
state: ""
});
setForms(existingForms);
};
const updateForm = useCallback(
(index, country, state) => {
const existingForms = [...forms];
existingForms[index].country = country;
existingForms[index].state = state;
setForms(existingForms);
},
[forms, setForms]
);
const printForm = () => {
console.log(forms);
};
return (
<>
<Button variant="contained" onClick={addForm}>
Add
</Button>
<Button variant="contained" onClick={printForm}>
Print
</Button>
<hr />
{forms
? forms.map((form, index) => (
<CountryStateFormItems
key={index}
index={index}
form={form}
updateForm={updateForm}
/>
))
: ""}
</>
);
}
You can use two state variables for storing both countries. Eg:
const [selectedCountry1, setSelectedCountry2] = useState("");
const [selectedCountry2, setSelectedCountry2] = useState("");
And similarly, you can use 2 variables for storing the country's state values as well.
Alternatively, you can also store it in a single state object with multiple keys, something like:
const [selectedCountriesObject, setSelectedCountriesObject] = useState({ country1: "", country2: ""});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With