reactjs - MUI Chips som taggar som kan väljas (checkbox-liknande beteende)


Jag har en JSON-fil där jag hämtar datan om chipsen från

[
    { "id": "4", "name": "Caucasian" },
    { "id": "5", "name": "Asian" },
    { "id": "6", "name": "Middle Eastern" },
    { "id": "7", "name": "Eurasian" },
    { "id": "8", "name": "African" },
    { "id": "10", "name": "Caribbean" },
    { "id": "11", "name": "Scandinavian European" },
    { "id": "12", "name": "Afro American" },
    { "id": "13", "name": "Latin American" },
    { "id": "14", "name": "European" },
    { "id": "15", "name": "Slavic European" },
    { "id": "16", "name": "American Indian" },
    { "id": "17", "name": "Inuit" },
    { "id": "21", "name": "Aboriginal/Torres Strait" },
    { "id": "22", "name": "Maori" },
    { "id": "23", "name": "Pacific Islander" },
    { "id": "24", "name": "Indian" },
    { "id": "26", "name": "Cambodian" },
    { "id": "27", "name": "Korean" },
    { "id": "28", "name": "Vietnamese" },
    { "id": "29", "name": "Thai" },
    { "id": "30", "name": "Chinese" },
    { "id": "31", "name": "Malaysian" },
    { "id": "32", "name": "Filipino" },
    { "id": "33", "name": "Japanese" },
    { "id": "34", "name": "Indonesian" },
    { "id": "35", "name": "Singaporean" },
    { "id": "147", "name": "Fijian" },
    { "id": "148", "name": "Tongan" },
    { "id": "150", "name": "Cook Islander" },
    { "id": "156", "name": "Northern European" },
    { "id": "157", "name": "Latin European" },
    { "id": "158", "name": "Central American" },
    { "id": "159", "name": "South American Indian" },
    { "id": "160", "name": "Hawaiian" },
    { "id": "161", "name": "East African" },
    { "id": "162", "name": "West African" },
    { "id": "163", "name": "Northern African" },
    { "id": "164", "name": "Eskimo" },
    { "id": "165", "name": "Arabic" },
    { "id": "166", "name": "Southern African" }
]

Jag har kartlagt dessa data som MUI-chip

{chipData.map((c) => (
    <Chip label={c.name} key={c.id} onClick={handleClick} />
))}

Det jag behöver göra är att göra varje chip valbart och sedan få alla valda chip överförda för att matcha de chip som valts med hjälp av en autokomplettering vid ett knappklick.

Så i princip kommer det att finnas 2 sätt att välja Chipsen.

  1. Använda Autocomplete (Skriv och Välj)
  2. Från ett modal som visar alla chips och kunna välja det som en kryssruta (Klicka och välj)

Jag har försökt använda useState men problemet är att alla Chips delar på ett useState, så när en väljs/avmarkeras följer alla med.

Jag sitter lite fast här men kan inte tycka att jag hittar en liknande lösning på det jag försöker göra. Någon insikt/lösning skulle verkligen hjälpa!

Svar #1

function Chip({name, onClick, variant, children}){
    return (
        <div className={variant} onClick={onClick}>{children}</div>
    )
}

const listOfChips = [
  { id: "4", name: "Caucasian" },
  { id: "5", name: "Asian" },
  { id: "6", name: "Middle Eastern" },
  { id: "7", name: "Eurasian" },
  { id: "8", name: "African" },
  { id: "10", name: "Caribbean" },
  { id: "11", name: "Scandinavian European" },
  { id: "12", name: "Afro American" },
  { id: "13", name: "Latin American" },
  { id: "14", name: "European" },
  { id: "15", name: "Slavic European" },
  { id: "16", name: "American Indian" },
  { id: "17", name: "Inuit" },
  { id: "21", name: "Aboriginal/Torres Strait" },
  { id: "22", name: "Maori" },
  { id: "23", name: "Pacific Islander" },
  { id: "24", name: "Indian" },
  { id: "26", name: "Cambodian" },
  { id: "27", name: "Korean" },
  { id: "28", name: "Vietnamese" },
  { id: "29", name: "Thai" },
  { id: "30", name: "Chinese" },
  { id: "31", name: "Malaysian" },
  { id: "32", name: "Filipino" },
  { id: "33", name: "Japanese" },
  { id: "34", name: "Indonesian" },
  { id: "35", name: "Singaporean" },
  { id: "147", name: "Fijian" },
  { id: "148", name: "Tongan" },
  { id: "150", name: "Cook Islander" },
  { id: "156", name: "Northern European" },
  { id: "157", name: "Latin European" },
  { id: "158", name: "Central American" },
  { id: "159", name: "South American Indian" },
  { id: "160", name: "Hawaiian" },
  { id: "161", name: "East African" },
  { id: "162", name: "West African" },
  { id: "163", name: "Northern African" },
  { id: "164", name: "Eskimo" },
  { id: "165", name: "Arabic" },
  { id: "166", name: "Southern African" },
];

function App() {
  const [allChips, setAllChips] = React.useState(listOfChips);
  const [selected, setSelected] = React.useState(new Set());

  function handleSelectionChanged(id) {
    // treat state as immutable
    // React only does a shallow comparison so we need a new Set
    const newSet = new Set(selected);
    if (newSet.has(id)) newSet.delete(id);
    else newSet.add(id);
    setSelected(newSet);
  }

  return (
    <React.Fragment>
      {allChips.map((c) => (
        <Chip
          key={c.id}
          onClick={() => handleSelectionChanged(c.id)}
          variant={selected.has(c.id) ? "filled" : "outlined"}
        >
          {c.name}
        </Chip>
      ))}
    </React.Fragment>
  );
}
ReactDOM.render(<App />, document.getElementById("root"));
div {
  border-radius: 20px;
  padding: 10px;
  margin: 10px;
}

.filled {
   background-color: black;
   color: white;
   border: 1px solid white;
}

.outlined {
   background-color: white;
   color: black;
   border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>

<div id="root"></div>

Ett sätt (det finns andra sätt) är att använda ett Set som innehåller alla ID:n som redan är valda och beroende på om ett ID finns i Set:et eller inte kan du göra någon villkorsstyrd rendering.

Jag har skapat en komponentchip här för att visa att det fungerar, du kan förstås använda MUI Chip och använda variant-egenskapen, eller något annat förstås.

När en Chip klickas kontrollerar du bara om den finns inom Mängden. Om den inte finns, lägg till den. Om den finns, ta bort den.

När det gäller att skriva i ett inmatningsfält är tillvägagångssättet ganska enkelt. Du behöver ett inmatningsfält och på onChange behöver du söka efter möjliga chips med name, t.ex. genom att använda startsWith() (eller någon mer avancerad autokomplettering), och så snart användaren väljer ett specifikt chip kommer du att ha id för det chipet och kan anropa handleSelectionChanged(id) med det ID:t. Detta skulle naturligtvis innebära att om du skriver det två gånger kommer det att tas bort igen, men du kan självklart ändra logiken för detta också.