'React - How to filter a list without losing data?

I'm able to filter the list to display the items I want. The problem is, my method of filtering actually alters the list rather than just display the items I'm after. I believe this is because I'm actually using setState to accomplish the filtering, so I need to use a method that keeps my original list and simply implements the filters rather than changing the state.

Here is the code for the functions I'm using to filter:

  filAll = () => {
    this.setState({
      todos: this.state.todos
    });
  }

  filActive = () => {
    this.setState({
      todos: this.state.todos.filter(todo => !todo.completed)
    });
  }

  filComplete = () => {
    this.setState({
      todos: this.state.todos.filter(todo => todo.completed)
    });
  }

Someone had mentioned that I need my full list in props as opposed to state, but I don't really understand what that means or how to implement it.

Thanks.



Solution 1:[1]

You could perform the filter inside the render function, without updating the state. You then have access to the filtered state when you render, but you never modify the original state.

class myComponent extends React.Component {
    render() {
        let filteredComplete = this.state.todos.filter(todo => todo.completed)
        // Use the filtered state here
        return ...
    }
}

Solution 2:[2]

You simply need two state variables. allTodos and todos. Use allTodos to filter or process the data and todos will be displayed/rendered

costructor(props) {
  const todos = [...props.todos];
  this.state = {
     allTodos: todos,
     todos: todos
  };
}

filActive = () => {
  this.setState({
    todos: this.state.allTodos.filter(todo => !todo.completed)
  });
}

Solution 3:[3]

for those ones who are trying to perform this effect using hooks, see my suggestion below:

const data = [{id:1, description: "hey there" }, {id:2, description: "last meeting"}, {id:3, description: "thank god it's friday"}]

    export default function App(){
       const [items, setItems] = useState(data);
       const [term, setTerm] = useState('');

      return (
        <div className="App">
          <h1>Filter List React</h1>
          <input type='text' onChange={(e)=> setTerm(e.target.value)} />
          <ul>
          {items.filter((item)=> item.description.includes(term)).map(({id, description})=>{
            return <li key={id}>{description}</li> 
          })}
          </ul>
        </div>
      ); 
    }

Since term is initialized as an empty string, filter will match all items in the description property and then the map will retrieve all items. Once the user starts typing, filter will retrieve items which description matchs the term and the map will do the job.

I like this approach because there is no need to set and manage two states.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Viktor W
Solution 2 Zohaib Ijaz
Solution 3 Felipe