MUI Chips as tags that can be selected (checkbox like behavior)

Issue

I have a JSON file where I am getting the data of the chips from

[
    { "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" }
]

I have mapped out this data as MUI chips

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

What I need to do is make each Chip selectable and then get all the selected Chip transferred to match the Chips selected using a Autocomplete with a button click.

So basically, there will be 2 ways to select the Chips.

  1. Using Autocomplete (Type and Select)
  2. From a modal that shows all the chips and be able to select it like checkbox (Click and Select)

I have tried using useState but the problem is all the Chips are sharing one useState so whenever one is selected/unselected, all follows.

I am kind of stuck here but can’t seem to find a similar solution on what I am trying to do.
Any insight/solution would really help!

Solution

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>

One way (there are other ways) is to use a Set which holds all the IDs that are already selected and based on what if an ID is in the Set or not you could do some conditional rendering.

I have created a component chip here to show that it works you can of course use the MUI Chip and use the variant prop, or something else of course.

Whenever a Chip is clicked you just check whether it is within the Set. If it is not, add it. If it is, remove it.

As far as your typing in an input field goes the approach is quite straighforward. You need an input field and onChange you need to search for the possible chips by name e.g. using startsWith() (or some more advanced auto-completion) and as soon as the user selects a specific chip you will have the id of that chip and can call handleSelectionChanged(id) with that ID. This would of course mean that if you type it twice it will be removed again, but you could certainly change the logic for this as well.

Answered By – Mushroomator

Answer Checked By – Mary Flores (AngularFixing Volunteer)

Leave a Reply

Your email address will not be published.