Home > Blockchain >  Filtering a todo list based on button clicked on React
Filtering a todo list based on button clicked on React

Time:10-08

I'm currently working on a to-do list app. Currently, I'm able to add, delete and edit the to-do list. I have a problem filtering my to-do list based on categories. The categories I have are all, active and completed. I'm stuck trying to filter the selected list based on the button clicked.

App.jsx:

import './App.css'
import Todo from './components/Todo';
import FilterButton from './components/FilterButton';
import Form from './components/form';
import { nanoid } from "nanoid";


function usePrevious(value) {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
}

const filterMap = {
  All: () => true,
  Active: (task) => !task.completed,
  Completed: (task) => task.completed
};
const filterNames = Object.keys(filterMap);

function App(props) {
  const [tasks, setTasks] = useState(props.tasks); 
  const [filter, setFilter] = useState('ALL');
 
  function toggleTaskCompleted(id) {
    const updatedTasks = tasks.map((task) => {
      // if this task has the same ID as the edited task
      if (id === task.id) {
        // use object spread to make a new object
        // whose `completed` prop has been inverted
        return {...task, completed: !task.completed}
      }
      return task;
    });
    setTasks(updatedTasks);
  }


  
  function deleteTask(id) {
    const remainingTasks = tasks.filter((task) => id !== task.id);  
  setTasks(remainingTasks);
    }
  function editTask(id, newName) {
    const editedTaskList = tasks.map((task) => {
    // if this task has the same ID as the edited task
      if (id === task.id) {     

        return {...task, name: newName}
      }
      return task;
    });
    setTasks(editedTaskList);
  }

  const taskList =tasks
  .filter((filterNames[filter]))
  .map((task)=> (
    <Todo
      id={task.id}
      name={task.name}
      completed={task.completed}
      key={task.id}
      toggleTaskCompleted={toggleTaskCompleted}
      deleteTask={deleteTask}
      editTask={editTask}
    />
  ));
  
  const filterList = filterNames.map((name) => (
    <FilterButton
      key={name}
      name={name}
      isPressed={name === filter}
      setFilter={setFilter}
    />
  ));

  function addTask(name) {
    const newTask = { id: `todo-${nanoid()}`, name, completed: true };
    setTasks([...tasks, newTask]);
  }
  const tasksNoun = taskList.length !== 1 ? 'tasks' : 'task';
  const headingText = `${taskList.length} ${tasksNoun} remaining`; 
  
  const listHeadingRef = useRef(null);
  const prevTaskLength = usePrevious(tasks.length);

  useEffect(() => {
    if (tasks.length - prevTaskLength === -1) {
      listHeadingRef.current.focus();
    }
  }, [tasks.length, prevTaskLength]);
  

  return (
    <div className="todoapp stack-large">
      <h1>TodoApp</h1>
      <Form addTask={addTask} />
 
      <div className="filters btn-group stack-exception">
      {filterList}

      </div>
      <h2 id="list-heading" tabIndex="-1" ref={listHeadingRef}>
      {headingText}
      </h2>
      <ul
        role="list"
        className="todo-list stack-large stack-exception"
        aria-labelledby="list-heading"
      >
      {taskList}
      </ul>
    </div>
  );
}



export default App;

FilterButton

''import React from "react";

function FilterButton(props) {
  return (
    <button
      type="button"
      className="btn toggle-btn"
      aria-pressed={props.isPressed}
      onClick={() => props.setFilter(props.name)}
    >
      <span className="visually-hidden">Show </span>
      <span>{props.name}</span>
      <span className="visually-hidden"> tasks</span>
    </button>
  );
}

export default FilterButton;  ```

CodePudding user response:

You defined the filter state as:

const [filter, setFilter] = useState('ALL');

And you update the filter state inside FilterButton.

But this just updates the state without changing the displayed list. You can add define a useEffect that is triggered when the filter state changes:

useEffect(() => {
   setTasks(tasks.filter(filterMap[filter]));
}, [filter, tasks]);

CodePudding user response:

You're passing filterName that actually only contains the keys, not the method.

const taskList = useMemo(()=>tasks
  .filter(filterMap[filter])
  .map((task)=> (
    <Todo
      .....
    />
  )),[tasks,filter]);

Also just wrap your taskList with useMemo so whenever tasks & filter change your taskList will be updated.

  • Related