TO-DO APP USING REACT, VITE, AND BOOTSTRAP - PART 4: EDIT TASKS

In this part of the tutorial, we'll implement the ability to edit 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

Edit the Task

To enable editing, we'll need an edit button that will open a modal form.

First, in the Task component, after the task title, let's add a div element. Within this element, we'll add an edit button:

<div className="ms-auto">
  <button className="btn btn-info btn-sm">Edit</button>
</div>

We'll put the modal in the Tasks component below the tasks list. We'll use the Modal component from the react-bootstrap library. It provides a convenient and pre-styled solution for creating modals in React applications. Let's import it at the top of the file:

import Modal from "react-bootstrap/Modal";

Then, we can use it. Below the tasks lists put this:

<Modal>
  <Modal.Header closeButton>
    <Modal.Title>Edit Task</Modal.Title>
  </Modal.Header>
  <Modal.Body></Modal.Body>
</Modal>

For now, our modal only has only a title. 

Currently, the modal is hidden, so a state variable is necessary to toggle its visibility.  In the Tasks component, let's add such a variable after the tasks state variable:

const [showEdit, setShowEdit] = useState(false);

By setting the initial state to false, the modal is initially hidden when the component renders. 

Now we can add these props to our modal:

<Modal show={showEdit} onHide={() => setShowEdit(false)}>
  ....
</Modal>

The show prop determines whether the modal is displayed based on the value of the showEdit state variable, and the onHide prop specifies a function to be called when the modal is hidden, in this case, setting the showEdit state to false.

Then, we will set this variable state to true, when the edit button is clicked. In the Task component, we'll define a handleTaskEdit function:

const handleTaskEdit = (task) => {
  setShowEdit(true);
};

To ensure that the handleTaskEdit function can access and update the showEdit state in the parent component (Tasks), we're passing down the setShowEdit prop to the Task component.

const Task = ({ task, setShowEdit }) => {
  ...
};

In the Tasks component, we update the Task component by passing the setShowEdit prop to it:

<Task key={task.id} task={task} setShowEdit={setShowEdit} />

And we attach an onClick event handler to the Edit button within the Task component. This event handler triggers the handleTaskEdit function:

<button
  className="btn btn-info btn-sm"
  onClick={() => handleTaskEdit(task)}
>
  Edit
</button>

When the user clicks the edit button, the showEdit state is updated to true, causing the modal to be displayed.

Create an EditTask Component

We will put the edit modal in its own component. In the src folder, let's create a file called EditTask.jsx. Let's import the Modal component from the react-bootstrap library:

import Modal from "react-bootstrap/Modal";

We can remove this import from the Tasks component.

And now let's create the EditTask component:

const EditTask = ({ showEdit, setShowEdit }) => {
  return (
    <Modal show={showEdit} onHide={() => setShowEdit(false)}>
      <Modal.Header closeButton>
        <Modal.Title>Edit Task</Modal.Title>
      </Modal.Header>
      <Modal.Body></Modal.Body>
    </Modal>
  );
};

export default EditTask;

Now, we'll import it into the Tasks component:

import EditTask from "./EditTask.jsx";

Then we can use it. Below the tasks lists, delete the Modal component add this:

<EditTask showEdit={showEdit} setShowEdit={setShowEdit} />

 Within the Task component's JSX structure, we've added the EditTask component, passing it props such as  showEdit and setShowEdit. By modularizing the edit task modal into its own component, we've improved code organization and maintainability. We can now easily manage the edit task functionality separately from other components, enhancing the overall structure of the application.

Create an Edit Task Form

In the body of the modal, we'll add the edit form:

<form>
  <label htmlFor="task-title" className="form-label">
    Task Title
  </label>
  <input
    type="text"
    name="task-title"
    id="task-title"
    className="form-control"
    required
  />
  <button type="submit" className="btn btn-primary mt-3">
    Save Changes
  </button>
</form>

We want to edit the task we clicked on, so we'll need a state variable to store this task ID. In the Tasks component, add this variable below the other state variables:

const [editedTaskId, setEditedTaskId] = useState(null);

Then, we pass the setEditedTaskId as a prop to the Task component:

const Task = ({ task, setShowEdit, setEditedTaskId }) => {
  ...
  );

 In the Tasks component, update the usage of the Task component:

<Task
  key={task.id}
  task={task}
  setShowEdit={setShowEdit}
  setEditedTaskId={setEditedTaskId}
/>

Now, in the handleTaskEdit function, we set the editedTaskId state to the ID of this task, ensuring that the correct task is targeted for editing.

const handleTaskEdit = (task) => {
  setShowEdit(true);
  setEditedTaskId(task.id);
};

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 Tasks component. 

We'll define a state variable named editedTask and a corresponding function setEditedTask to update its value. This state variable will hold the value of the input field. Below the other state variables add this:

const [editedTask, setEditedTask] = useState("");

Then, we pass the setEditedTask as a prop to the Task component:

const Task = ({ task, setShowEdit, setEditedTaskId, setEditedTask }) => {
	...
};

Let's update the usage of this component in the Tasks component:

<Task
  key={task.id}
  task={task}
  setShowEdit={setShowEdit}
  setEditedTaskId={setEditedTaskId}
  setEditedTask={setEditedTask}
/>

Then, inside the handleTaskEdit function, we set the editedTask variable to the value of the task title:

const handleTaskEdit = (task) => {
  setShowEdit(true);
  setEditedTaskId(task.id);
  setEditedTask(task.title);
};

We'll pass the editedTask and the setEditedTask props to the EditTask component:

const EditTask = ({ showEdit, setShowEdit, editedTask, setEditedTask }) => {
  ...
};

In the Tasks component, we'll update the usage of this component as follows:

<EditTask
  showEdit={showEdit}
  setShowEdit={setShowEdit}
  editedTask={editedTask}
  setEditedTask={setEditedTask}
/>

We'll update the input element to use the editedTask as value:

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

By initializing the editedTask state variable with the title of the task that was clicked on, we ensure that the input field is pre-filled with the current title when the modal is opened for editing.  Also, we use the onChange event handler to update the input value via e.target.value.

In the EditTask component, we'll create a submitEdit function that update the task:

const submitEdit = (e, id) => {
  e.preventDefault();
  if (editedTask.trim() === "") {
    console.error("Task title cannot be empty");
    return;
  }
  const updatedTasks = tasks.map((task) => {
    if (task.id === id) {
      return {
        ...task,
        title: editedTask.trim(),
      };
    } else {
      return task;
    }
  });
  setTasks(updatedTasks);
  setShowEdit(false);
};

This function iterates over the tasks list, and for the task with the matching ID, it updates the title with the value from editedTask. Then it sets the updated tasks list using setTasks and hides the edit modal by setting showEdit to false.

In order for this function to work, we'll need to pass the tasks and the setTasks props to the EditTask component:

const EditTask = ({
  showEdit,
  setShowEdit,
  editedTask,
  setEditedTask,
  tasks,
  setTasks,
}) => {
  ...
  };

We'll also update the usage of the EditTask component in the Tasks component:

<EditTask
  showEdit={showEdit}
  setShowEdit={setShowEdit}
  editedTask={editedTask}
  setEditedTask={setEditedTask}
  tasks={tasks}
  setTasks={setTasks}
/>

To update the task what was clicked on, we must pass the editedTaskId prop to the EditTask component:

const EditTask = ({
  showEdit,
  setShowEdit,
  editedTask,
  setEditedTask,
  tasks,
  setTasks,
  editedTaskId,
}) => {
  ...
};

In the Tasks component, we update the usage of the EditTask component:

<EditTask
  showEdit={showEdit}
  setShowEdit={setShowEdit}
  editedTask={editedTask}
  setEditedTask={setEditedTask}
  tasks={tasks}
  setTasks={setTasks}
  editedTaskId={editedTaskId}
/>

Then, we pass the submitEdit function to the onSumbit event handler of the edit form:

<form onSubmit={(e) => submitEdit(e, editedTaskId)}>
  ...
</form>

Post last updated on April 3, 2024