TO-DO APP USING REACT, VITE, AND BOOTSTRAP - PART 3: CREATE TASKS

In this post, we will implement the ability to add new tasks.

ALL PARTS:

  1. To-Do App Using React, Vite, And Bootstrap: Set Up the Application
  2. To-Do App Using React, Vite, And Bootstrap: Create Custom Components
  3. To-Do App Using React, Vite, And Bootstrap: Create Tasks
  4. To-Do App Using React, Vite, And Bootstrap: Edit Tasks
  5. To-Do App Using React, Vite, And Bootstrap: Delete and Update Tasks Status

Create Tasks

Let's now implement the ability to add new tasks. We'll create another component that we'll call NewTask. In the src folder, let's add a NewTask.jsx file where we'll put this component.

Let's edit this file as follows:

const NewTask = () => {
  return (
    <form className="mt-5">
      <label htmlFor="task-title" className="form-label">
        New Task
      </label>
      <input
       type="text"
       name="task-title"
       id="task-title"
       className="form-control"
       required
      />
      <button type="submit" className="btn btn-primary mt-3">
        Add
      </button>
    </form>
  );
};

export default NewTask;

Let's import this component into the Tasks component. Below the other import statements, add this:

import NewTask from "./NewTask.jsx";

Then we can use it. Above the tasks list, put this:

<NewTask />

Now, we want to add the new task to the list when the form is submitted. For that, we'll create a function named handleSubmit. This function will be attached to the onSubmit event handler of the form, triggering when the form is submitted by the user.

Let's add this inside the NewTask component, before the return statement:

const handleSubmit = (e) => {
  e.preventDefault();
};

Within this function, we call e.preventDefault() to prevent the default form submission behavior, which typically results in a page refresh. By preventing this default action, we can handle the form submission entirely within our React application without disrupting the user experience.

Next, we attach the onSubmit event handler to the form element in the JSX, specifying onSubmit={handleSubmit}

<form className="mt-5" onSubmit={handleSubmit}>
  ...
</form>;

This ensures that when the form is submitted (e.g., by clicking the submit button), the handleSubmit function will be invoked, allowing us to handle the form submission within our React application.

We want to get the value of the input field when the form is submitted. For this, we create a new state variable inside the NewTask component.  First, we need to import the useState hook from React:

import { useState } from "react";

Next, we'll define a state variable named task and a corresponding function setTask to update its value. This state variable will hold the value of the input field. Above the handleSubmit function add this:

 const [task, setTask] = useState("");

Now that we have the state variable set up, we can bind its value to the input field. We'll update the value attribute of the input element to reflect the value of the task state variable:

<input
  type="text"
  name="task-title"
  id="task-title"
  className="form-control"
  value={task}
  required
/>

By setting the value attribute to task, we ensure that the input field reflects the current value stored in the task state variable. This allows us to capture and manipulate the user input effectively.

When passing a value prop to an input field, it's essential to include an onChange event handler. This ensures that the input field's value can be updated based on user input. Without an onChange handler, the input field would remain static, and user input would not be reflected in the component's state.

To synchronize the input field's value with the component's state, we use the onChange event handler to capture user input and update the state accordingly. Inside the onChange handler, we access the value of the input field via e.target.value, where e is the event object. We then use this value to update the state using the setTask function.

Here's how the updated input element looks:

<input
  type="text"
  name="task-title"
  id="task-title"
  className="form-control"
  value={task}
  onChange={(e) => setTask(e.target.value)}
  required
/>

To update the tasks list, we must pass the tasks and the setTasks from the Tasks component to the NewTask component. Let's edit it: 

const NewTask = ({ tasks, setTasks }) => {
 // Additional component logic
};

By destructuring the tasks and setTasks props in the NewTask component's function parameters, we ensure that these values are accessible within the component. This enables the NewTask component to interact with the tasks list maintained by the parent Tasks component and update it as needed.

We'll pass the tasks state variable and the setTasks function as props to the NewTask component in the Tasks component, to enable NewTask to interact with and update the tasks list maintained by Tasks. Here's the updated code:

<NewTask tasks={tasks} setTasks={setTasks} />

Including these props allows for effective communication between parent and child components in React, facilitating the passing of data and functions as needed for seamless interaction and state management.

Now, we can update the list when a new task is added. First, let's import uuid in the NewTask component:

import { v4 as uuidv4 } from "uuid";

Now, let's update the handleSubmit function:

const handleSubmit = (e) => {
  e.preventDefault();
  if (task.trim() === "") {
    console.error("Task cannot be empty.");
    return;
  }
  setTasks([
    { id: uuidv4(), title: task.trim(), completed: false },
    ...tasks,
  ]);
  setTask("");
};

We first check if the trimmed task string is empty. If it is, we log an error message to the console and return early from the function.

If the task is not empty, we proceed to add it to the tasks array.

We use the uuidv4() function from the uuid library to generate a unique ID for each new task.

We construct a new task object with the generated ID, the trimmed title obtained from the task state variable, and a default completed value of false.

We prepend the new task to the existing tasks list using the spread operator (...tasks) within the setTasks function. This ensures that the new task is added to the beginning of the list.

After adding the task, we clear the input field by setting the task state variable to an empty string using setTask("").

Post last updated on June 8, 2024