Creating a Not To Do List App with React

Efficient Task Management with React: Building a 'Not To Do List' App

In this blog post, we will create a Not To Do List application using React. The application allows users to track and manage their tasks, ensuring they don't overcommit themselves. The app will display a list of tasks with their allocated hours and provide the ability to switch tasks between "entry" and "bad" categories. Let's dive into the code and see how it works.

Getting Started

To begin with, let's set up the basic structure of our React application. We'll create the main component App that will hold the state and manage the task list. We'll also import the necessary components and libraries we'll be using.

import React, { useState } from "react";
import { Table } from "./components/Table";
import { Form } from "./components/Form";
import { RandomStrGenerator } from './utility';
import "./App.css";

Next, we'll define the maximum number of hours per week (hrwk) as 7 * 24, which is 168 hours.

const hrwk = 7 * 24;

Now, let's define the App component. It will hold the state for the task list (taskList) using the useState hook. Additionally, we'll calculate the total allocated hours (hr) based on the task list. The addTask function allows adding a new task to the list, while the taskSwitcher and dlt functions handle switching task types and deleting tasks, respectively.

function App() {
  const [taskList, setTaskList] = useState([]);
  const hr = taskList.reduce((acc, item) => acc + +item.hr, 0);

  const addTask = (data) => {
    if (+data.hr + hr > hrwk) {
      return alert('Not enough time available.');
    }
    setTaskList([...taskList, data]);
  };

  const taskSwitcher = (id, type) => {
    const updatedTaskList = taskList.map((item) => {
      if (item.id === id) {
        item.type = type;
      }
      return item;
    });
    setTaskList(updatedTaskList);
  };

  const dlt = (id) => {
    const updatedTaskList = taskList.filter((item) => item.id !== id);
    setTaskList(updatedTaskList);
  };

  console.log(taskList);

  // Render the UI
  return (
    <div className="wrapper">
      <div className="container">
        {/* Title */}
        <div className="row">
          <div className="col text-center mt-5">
            <h1>Not To Do List</h1>
          </div>
        </div>

        {/* Form area */}
        <Form addTask={addTask} />

        {/* List area */}
        <Table taskList={taskList} taskSwitcher={taskSwitcher} dlt={dlt} />

        {/* Total hours area */}
        <div className="row fw-bold">
          <div className="col">
            The total hours allocated for this week is{" "}
            <span id="totalHrs">{hr}</span> Hours
          </div>
        </div>
      </div>
    </div>
  );
}

export default App;

Creating the Form Component

The Form component is responsible for rendering the form to add new tasks. It consists of an input for the task description, an input for the number of hours, and a submit button. Let's take a look at the implementation.

export const Form = ({ addTask }) => {
  const [frmDt, setFrmDt] = useState({
    type: "entry",
  });

  const handleOnChange = (e) => {
    const { name, value } = e.target;

    setFrmDt({
      ...frmDt,
      [name]: value,
    });
  };

  const handleOnSubmit = (e) => {
    e.preventDefault();
    const id = RandomStrGenerator(5);
    console.log(id);

    addTask({ ...frmDt, id });
  };

  return (
    <form
      onSubmit={handleOnSubmit}
      className="border p-3 rounded shadow-lg"
    >
      <div className="row mt-3 g-2">
        <div className="col-md-6">
          <input
            onChange={handleOnChange}
            type="text"
            name="task"
            className="form-control task-input"
            required
            placeholder="Enter a task..."
          />
        </div>
        <div className="col-md-3">
          <input
            onChange={handleOnChange}
            type="number"
            name="hr"
            className="form-control hrs-input"
            min="1"
            required
            placeholder="Enter no. of hrs"
          />
        </div>
        <div className="col-md-3 text-center">
          <button className="btn btn-primary" type="submit" id="form-btn">
            <i className="fa-solid fa-plus"></i> Add New Task
          </button>
        </div>
      </div>
    </form>
  );
};

The Form component receives the addTask function as a prop, which allows it to update the task list in the parent component. When the form is submitted, the handleOnSubmit function is called, preventing the default form submission behavior. A unique ID is generated using the RandomStrGenerator utility function, and the task data along with the ID is passed to the addTask function.

Creating the Table Component

The Table component is responsible for displaying the task list and providing options to switch task types and delete tasks. It consists of two sections: "Entry List" and "Bad List." Let's see how it's implemented.

javascriptCopy codeexport const Table = ({ taskList, taskSwitcher, dlt }) => {
  const entryList = taskList.filter((item) => item.type === "entry");
  const badList = taskList.filter((item) => item.type === "bad");

  return (
    <div className="row mt-5 g-2">
      <div className="col-md">
        <h2 className="text-center">Entry List</h2>
        <hr />
        <table className="table table-striped table-hover">
          <tbody id="task-list">
            {entryList.map((item, i) => (
              <tr key={i}>
                <td>{i + 1}</td>
                <td>{item.task}</td>
                <td>{item.hr} hr(s)</td>
                <td className="text-end">
                  <button onClick={() => dlt(item.id)} className="btn btn-danger">
                    <i className="fa-solid fa-trash"></i>
                  </button>
                  {" "}
                  <button onClick={() => taskSwitcher(item.id, 'bad')} className="btn btn-success">
                    <i className="fa-solid fa-right-long"></i>
                  </button>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
      <div className="col-md">
        <h2 className="text-center">Bad List</h2>
        <hr />
        <table className="table table-striped table-hover">
          <tbody id="task-list">
            {badList.map((item, i) => (
              <tr key={i}>
                <td>{i + 1}</td>
                <td>{item.task}</td>
                <td>{item.hr} hr(s)</td>
                <td className="text-end">
                  <button onClick={() => taskSwitcher(item.id, 'entry')} className="btn btn-warning">
                    <i className="fa-solid fa-left-long"></i>
                  </button>
                  {" "}
                  <button onClick={() => dlt(item.id)} className="btn btn-danger">
                    <i className="fa-solid fa-trash"></i>
                  </button>
                </td>
              </tr>
            ))}
          </tbody>
        </table>

        <div className="text-end fw-bold">
          You can save <span id="totalBadHrs">{badList.reduce((acc, { hr }) => acc + +hr, 0)}</span> Hours
        </div>
      </div>
    </div>
  );
};

The Table component receives three props: taskList, taskSwitcher, and dlt. It filters the taskList based on the task type to create two separate lists: entryList and badList. These lists are then mapped over to generate the rows for the respective tables. Each row displays the task name, duration, and buttons to delete the task or switch its type. The buttons call the corresponding functions passed through the props.

Implementing Utility Functions

The code includes a utility function called RandomStrGenerator that generates a random string of a given length. This function is used to generate unique IDs for the tasks. Here's how it's implemented:

javascriptCopy codeexport const RandomStrGenerator = (length) => {
  let str = "";

  const strcollection = 'qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnm';

  for (let i = 0; i < length; i++) {
    const charPosition = Math.round(Math.random() * strcollection.length);

    str += strcollection[charPosition];
  }
  return str;
};

The RandomStrGenerator function takes a length parameter and creates a random string by picking characters from a predefined collection of characters. The length of the string is determined by the length parameter passed to the function. The generated string is then returned.

Conclusion

In this blog post, we explored a "Not To Do List" application built using React. The application allows users to add tasks with durations, categorize them as "entry" or "bad," switch task types, delete tasks, and displays the total hours allocated for the week. We examined the implementation of the main App component, the Form component for adding tasks, the Table component for displaying the task list, and a utility function for generating random strings. This application serves as a useful tool for managing and organizing tasks effectively. Feel free to modify and enhance the code to suit your specific needs. Happy task management!