Reputation: 3785
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?
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!
Upvotes: 3
Views: 1584
Reputation: 301
You need to manage an array of objects in your state. Here's a good resource that explains how to do it: Managing State Array. I hope it helps!
Upvotes: 1
Reputation: 2925
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}
/>
))
: ""}
</>
);
}
Upvotes: 4
Reputation: 35
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: ""});
Upvotes: 2