Festina
Festina

Reputation: 325

How to rewrite state correctly in filters React

I need to use an input filter in React. I have a list of activities and need to filter them like filters on the picture. If the icons are unchecked, actions with these types of activities should not be showed. It works.

The problem that when I use input filter and write letters it works. But when I delete letter by letter nothing changes.

I understand that the problem is that I write the result in the state. And the state is changed.But how to rewrite it correctly. filter example

const [activities, setActivities] = useState(allActivities);
  const [value, setValue] = useState(1);
  const [checked, setChecked] = useState<string[]>([]);

  const switchType = (event: React.ChangeEvent<HTMLInputElement>)=> {
    const currentIndex = checked.indexOf(event.target.value);
    const newChecked = [...checked];

    if (currentIndex === -1) {
      newChecked.push(event.target.value);
    } else {
      newChecked.splice(currentIndex, 1);
    }

//function that shows activities if they are checked or unchecked
    
setChecked(newChecked);

    const res = allActivities.filter(({ type }) => !newChecked.includes(type));
    setActivities(res);
  };
//shows input filter
  const inputSearch = (event: React.ChangeEvent<HTMLInputElement>) => {
    
    const foundItems = activities.filter(
      (item) => item.activity.indexOf(event.target.value) > -1
    );

    setActivities(foundItems);
  };
//shows participants filter
  const countSearch = (event: React.ChangeEvent<HTMLInputElement>) => {
    setValue(Number(event.target.value));
    const participantsSearch = allActivities.filter(
      (item) => item.participants >= event.target.value
    );
    setActivities(participantsSearch);
  };

This is render part

  <Input
            onChange={inputSearch}       
               startAdornment={
              <InputAdornment position="start">
                <SearchIcon />
              </InputAdornment>
            }
          />
 <Input
            onChange={countSearch}
            type="number"
            value={props.value}
                      startAdornment={
              <InputAdornment
                position="start"
                className={classes.participantsTextField}
              >
                <PersonIcon />
              </InputAdornment>
            }
          />

Upvotes: 1

Views: 262

Answers (2)

Drew Reese
Drew Reese

Reputation: 202864

The issue here is that you save over the state that you are filtering, so each time a filter is applied the data can only decrease in size or remain the same. It can never reset back to the full, unfiltered data.

Also with the way you've written the checkbox and input callbacks you can't easily mix the two.

Since the filtered data is essentially "derived" state from the allActivities prop, and the value and checked state, it really shouldn't also be stored in state. You can filter allActivities inline when rendering.

const [value, setValue] = useState<sting>('');
const [checked, setChecked] = useState<string[]>([]);

const switchType = (event: React.ChangeEvent<HTMLInputElement>)=> {
  const currentIndex = checked.indexOf(event.target.value);

  if (currentIndex === -1) {
    setChecked(checked => [...checked, event.target.value]);
  } else {
    setChecked(checked => checked.filter((el, i) => i !== currentIndex);
  }
};

const inputSearch = (event: React.ChangeEvent<HTMLInputElement>) => {
  setValue(event.target.value.toLowerCase());
};

...

return (
  ...

  {allActivites.filter(({ activity, type }) => {
      if (activity || type) {
        if (type) {
          return checked.includes(type);
        }
        if (activity) {
          return activity.toLowerCase().includes(value);
        }
      }
      return true; // return all
    })
    .map(.....

Upvotes: 1

Roi
Roi

Reputation: 97

The problem lies with your inputSearch code. Each time you do setActivities(foundItems); you narrow down the state of your activities list. So when you start deleting, you don't see any change because you removed the rest of the activities from the state.

You'll want to take out allActivities into a const, and always filter allActivities in inputSearch, like so:

const allActivities = ['aero', 'aeroba', 'aerona', 'aeronau'];
const [activities, setActivities] = useState(allActivities);

// ...rest of your code

const inputSearch = (event: React.ChangeEvent<HTMLInputElement>) => {
  const foundItems = allActivities.filter(
    (item) => item.activity.indexOf(event.target.value) > -1
  );
  setActivities(foundItems);
};
// ...rest of your code

Upvotes: 0

Related Questions