简体   繁体   中英

How to populate State dropdown data as per selected Country dropdown two times in a single component in React.js?

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>
        &nbsp;
        <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>
      &nbsp;
      <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: ""});

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!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM