Search code examples
javascriptreactjstypescriptrefactoringreact-props

React Typescript: Can't filter an array after refactoring code


I am building a To Do list project using React and TypeScript. In the process of refactoring my code and breaking up different divs (Active tasks, Done tasks, All tasks) into separate files within my component folder, my project is no longer able to filter out tasks that are "Done" or "Active" (this is determined by the checkbox value that is in my ToDoItem file, it has been condensed to a prop: onUpdateToDo) I am confused and stuck over where to filter out my ToDoItem within my "Done" file as I believe that it should be mapped out within the visibleTodos function. The visibleToDos function is in my App.tsx file.

App.tsx

  const initialTodosString = localStorage.getItem("toDoList");
  
  const initialTodos = initialTodosString
  ? JSON.parse(initialTodosString)
  : [myToDo1, myToDo2];


  export function App(): JSX.Element {
  const [toDos, setToDos] = useState<ToDo[]>(initialTodos);
  const [addingToDo, setAddingToDo] = useState(false);
  const [showingDoneTasks, setShowDoneTasks] = useState(false); // show true if not
  const [showingActiveTasks, setShowActiveTasks] = useState(false); // show true if not
  const [showingAllTasks, setShowAllTasks] = useState(false);
  const [filterTodosCompleted, setFilterTodosCompleted] = useState<any | null>(
    null
  );

// VISIBLE TO DOS FUNCTION BELOW //

  const visibleTodos = useMemo(
    () =>
      toDos.filter((toDo) => {
        if (filterTodosCompleted === null) {
          return true;
        }
        return filterTodosCompleted === toDo.checked;
      }),
    [filterTodosCompleted, toDos]
  );

  useEffect(
    function () {
      localStorage.setItem("toDoList", JSON.stringify(toDos));
    },
    [toDos]
  );

// Showing Active Tasks function //

  function showAllTasks(){
    setShowAllTasks(true)
    setShowDoneTasks(false);
    setShowActiveTasks(false);
  }

  if (showingAllTasks) return (
    <AllTasks newTask={newTask}
    showActive={showActive}
    toDos={toDos}
    showDone={showDone}
    visibleTodos={toDos}
    setToDos={setToDos}/>
  )

  function showActive() {
    setShowActiveTasks(true);
    setFilterTodosCompleted(false);
    setShowDoneTasks(false);
  }

  function newTask() {
    setAddingToDo(true);
  }

  if (showingActiveTasks) {
    return (
      <ActiveTasks
        newTask={newTask}
        showActive={showActive}
        toDos={toDos}
        showDone={showDone}
        visibleTodos={toDos}
      />
    );
  }

/// showing Done Tasks function and jsx component with props being passed within

  function showDone() {
    setShowDoneTasks(true);
    setFilterTodosCompleted(true);
    setShowActiveTasks(false);
  }

  if (showingDoneTasks) {
    return (
      <DoneTasks
        newTask={newTask}
        showActive={showActive}
        toDos={toDos}
        showDone={showDone}
        visibleTodos={toDos}
        setToDos={setToDos}
        showAllTasks={showAllTasks}
      />
    );
  }
 if (addingToDo) {
    return (
      <NewTask
        newTask={newTask}
        showActive={showActive}
        toDos={toDos}
        showDone={showDone}
        visibleTodos={toDos}
        handleFormSubmit={handleFormSubmit}
      />
    );
  }

  function handleFormSubmit(event: FormEvent<HTMLFormElement>) {
    const data = Object.fromEntries(
      new FormData(event.target as HTMLFormElement)
    );
    const newDueDate = new Date(data.Date as string);
    const timeZoneCorrectedDate = new Date(
      newDueDate.getTime() + newDueDate.getTimezoneOffset() * 60 * 1000
    ); //
    debugger;
    setToDos([
      ...toDos,
      {
        title: data.Title as string,
        priority: parseInt(data.Priority as string) as 2 | 1,
        description: data.Description as string,
        checked: false,
        duedate: data.Date ? timeZoneCorrectedDate.getTime() : undefined,
      },
    ]);
  }

  return (
    <div className="App">
      <div className="greeting-container">
        <div className="greeting">
          <Greeting />
        </div>
        <button className="task-button" onClick={newTask}>
          New Task
        </button>
        <div className="date-container">
          Today is {new Date().toLocaleString("en-US", { weekday: "long" })}
          <br />
          <div className="current-date">
            {new Date().toLocaleString("en-US", {
              month: "long",
              day: "2-digit",
            })}
            , {new Date().getFullYear()}
          </div>
        </div>
      </div>
      <div className="task-container">
        <div className="task-counter">
          {toDos.length} {toDos.length === 1 ? "Task" : "Tasks"}
        </div>
        <div className="status-container">
          <button id="allButton" onClick={showAllTasks}>
            All
          </button>
          <button id="activeButton" onClick={showActive}>
            Active
          </button>
          <button id="doneButton" onClick={showDone}>
            Done
          </button>
        </div>
      </div>
      <hr />
      {showingDoneTasks ? (
        <DoneTasks
          newTask={newTask}
          showActive={showActive}
          toDos={toDos}
          showDone={showDone}
          visibleTodos={toDos}
          setToDos={setToDos}
          showAllTasks={showAllTasks}/>) : null}



      {visibleTodos.map((toDoItem: ToDo) => (
        <ToDoItem
          onDeleteToDo={function () {
            const updatedToDos = toDos.filter((x) => x !== toDoItem);
            setToDos(updatedToDos);
          }}
 onUpdateTodo={function (updates: any) {
            const updatedToDos = toDos.map((x) =>
              x === toDoItem ? ({ ...x, ...updates } as any) : x
            );
            console.log(updates);
            setToDos(updatedToDos);
          }}
          toDo={toDoItem}
        />
      ))}
    </div>
  );
}


Done.tsx

import { ToDo } from "../types/ToDo";
import { Greeting } from "./Greeting";
import { ToDoItem } from "./ToDoItem";

export const DoneTasks = (props:{
  newTask: () => void
  showActive: () => void
  toDos: ToDo[];
  showDone: () => void
  visibleTodos: ToDo[];
  setToDos: (value: React.SetStateAction<ToDo[]>) => void
  showAllTasks: () => void


}) => (
  <div className="App">
    <div className="greeting-container">
      <div className="greeting">
        <Greeting />
      </div>
      <button className="task-button" onClick={props.newTask}>
        New Task
      </button>
      <div className="date-container">
        Today is {new Date().toLocaleString("en-US", { weekday: "long" })}
        <br />
        <div className="current-date">
          {new Date().toLocaleString("en-US", {
            month: "long",
            day: "2-digit",
          })}
          , {new Date().getFullYear()}
        </div>
      </div>
    </div>
    <div className="task-container">
      <div id="completed-task-counter">
        {props.toDos.length}{" "}
        {props.toDos.length === 1 ? "Completed Task" : "Completed Tasks"}
      </div>
      <div className="status-container">
      <button id="allButton" onClick={props.showAllTasks}>
            All
          </button>
        <button className="activeButton" onClick={props.showActive}>
          Active
        </button>
        <button className="doneButton" onClick={props.showDone}>
          Done
        </button>
      </div>
    </div>
    <hr />


    {props.visibleTodos.map((toDoItem: ToDo) => (
        <ToDoItem
          onDeleteToDo={function () {
            const updatedToDos = props.toDos.filter((x) => x !== toDoItem);
            props.setToDos(updatedToDos);

          }}
         
          onUpdateTodo={function (updates: any) {
            const updatedToDos = props.toDos.map((x) =>
              x === toDoItem ? ({ ...x, ...updates } as any) : x
            );
            console.log(updates);
            props.setToDos(updatedToDos);
          }}
          
          toDo={toDoItem}
        />
      ))}
    </div>
  );
 

ToDoItem.tsx

export function ToDoItem(props: {
  toDo: ToDo;
  onDeleteToDo: any;
  onUpdateTodo: any;
}) {
  const handleOptionsChange = (event: any) => {
    const selectBox = event.target;
    const newValue = selectBox.value;
    const newPriority = parseInt(newValue);
  
    props.onUpdateTodo({ priority: newPriority })
  };
  const checkBoxCheck = (event: any) => {
    const checkBox = event.currentTarget.checked;
    const newCheckBoxValue = checkBox;

    props.onUpdateTodo({ checked: newCheckBoxValue })
    console.log(newCheckBoxValue)
  };
  const handleDateChange =(event: any) => {
    const newDate = event.target.value;

    props.onUpdateTodo({ duedate: newDate })

    console.log(newDate)
  }

  if (!props.toDo) {
    return <p>Missing To Do</p>;
  }

  return (
    <div
      className="to-do-item"
      data-priority={props.toDo.priority}
      id="to-do-item"
    >
      <div className="checkbox-title-container">
        <div className="check-title-div">
          <div>
            <input
              type="checkbox"
              id="checkbox"
              onChange={checkBoxCheck}
              checked={props.toDo.checked}
            />
          </div>

          <h2 className="to-do-title">{props.toDo.title}</h2>
        </div>
        <div id="delete-div">
          <select
            name="Priority"
            className="select-field"
            value={props.toDo.priority}
            onChange={handleOptionsChange}
          >
            <option value="1">Important</option>
            <option value="2">Normal</option>
          </select>
          <button id="delete" onClick={props.onDeleteToDo}>
            Delete
          </button>
        </div>
        <span className="to-do-date-container">
        <input name="Date" type="date" className="to-do-date-input" value={props.toDo.duedate
            ? new Date(props.toDo.duedate).toISOString().split('T')[0]
            : undefined} onChange={handleDateChange}/>
         
        </span>
        <div className="description-box">
          <span className="description">{props.toDo.description}</span>
        </div>
      </div>
      <br />
    </div>
  );
}

I am unsure if I am passing the wrong props into DoneTasks? Before I refactored my code, my ShowDone() function worked fine, for some reason the conditions within the showDone function are not being called and I am getting a duplicate of my App.tsx file when it should be returning my ToDoItems that are checked.


Solution

  • The answer is in the line below, each toDo item is being filtered through the condition that it is checked or not, then the map function displays each one:

      {props.toDos
        .filter((x) => x.checked === true)
        .map((toDoItem: ToDo) => (
    

    Rest of code below:

              <ToDoItem
                onDeleteToDo={function () {
                  const updatedToDos = props.toDos.filter((x) => x !== toDoItem);
                  props.setToDos(updatedToDos);
                }}
                onUpdateTodo={function (updates: any) {
                  const updatedToDos = props.toDos.map((x) =>
                    x === toDoItem ? ({ ...x, ...updates } as any) : x
                  );
                  props.setToDos(updatedToDos);
                }}
                toDo={toDoItem}
              />
            ))}