ReactJS - Filtering items from a list in a select dropdown

Viewed 145

What I am currently trying to do is to filter an existing list of items by the select ones in a select dropdown. Meaning that, whenever I click on a item, I want to remove it from the available options (so I can't add it twice to the list).

The problem that I'm having is that whenever I filter this item out, I don't display it on the screen (cause I've removed it from the list). How can I remove an item from the list of available options but still display it on the screen? Also if I decide to change the item again, will the previous one show in the list again?

I suspect the issue might be in my filtering function:

  const filterElements = (selectedPerson) => {
    let filteredList = people.filter((person) => {
      return selectedPerson.id !== person.id;
    });
    setPeople(filteredList);
  };

Here is a small code sandbox I've built as an example.

3 Answers

I would suggest that you try to explain the problem a little better: maybe I didn't get it right, but here goes my solution.

Seems like you always want to show all options in the dropdown, even if they are already selected. To do so, simply keep a list of all the options, and render each of them in the UI, even if they were already selected (filtered) by the user. Try this:

const allPeople = [
    { id: 1, name: "Mark" },
    { id: 2, name: "Peter" },
    { id: 3, name: "John" },
    { id: 4, name: "David" },
    { id: 5, name: " Karen" },
    { id: 6, name: "Johanes" }
]

const [people, setPeople] = useState(allPeople);

const renderOptions = () =>
    allPeople.map((person) => {  //... your UI));
});

It seems you want to filter out already selected choices from the other inputs, but not the input that has a given choice already selected.

You can do this by removing the filterElements function entirely and modifying your renderOptions function so that it returns only options that aren't currently selected (unless selected by the current input).

Here's how renderedOptions can look:

const renderOptions = (selected) => {
  const filteredPeople = people.filter(e => e.name === selected || ![selectedOne, selectedTwo, selectedThree].includes(e.name));
  return filteredPeople.map((person) => {
    return (
      <MenuItem value={person.name}>
        {person.name}
      </MenuItem>
    );
  });
}

The first condition in the filter callback checks if the option is selected by this input and includes the option if so, the second condition checks if the option is selected by any input and removes the option if so. All other options will be included.

Then call renderOptions like this:

<Select
  name="name"
  value={selectedOne}
  onChange={(e) => setSelectedOne(e.target.value)}
  input={<Input id="name" />}
>
  {renderOptions(selectedOne)}
</Select>
<Select
  name="name"
  value={selectedTwo}
  onChange={(e) => setSelectedTwo(e.target.value)}
  input={<Input id="name" />}
>
  {renderOptions(selectedTwo)}
</Select>
<Select
  name="name"
  value={selectedThree}
  onChange={(e) => setSelectedThree(e.target.value)}
  input={<Input id="name" />}
>
  {renderOptions(selectedThree)}
</Select>
``

This way the selected options aren't removed entirely, just hidden from those inputs that should not have access to them.

It would be simpler to add isVisible property flag to each person, and instead of filtering, just update the selected person with isVisible = false

  const [people, setPeople] = useState([
    { id: 1, name: "Mark", isVisible: true },
    { id: 2, name: "Peter", isVisible: true },
    { id: 3, name: "John", isVisible: true },
    { id: 4, name: "David", isVisible: true },
    { id: 5, name: " Karen", isVisible: true },
    { id: 6, name: "Johanes", isVisible: true }
  ]);

  const filterElements = (selectedPerson) => {
    const index = people.findIndex(p => p.id === selectedPerson.id);
    selectedPerson[index].isVisible = false;
    setPeople(selectedPerson);
  };

and here just do:

const renderOptions = () =>
  people.filter(p => p.isVisible).map((person) => {
    return (
      <MenuItem value={person.name} onClick={() => filterElements(person)}>
        {person.name}
      </MenuItem>
    );
 });
Related