In the Loop with React: Crafting Your First Basic Todo List
In this tutorial, we'll walk through the process of creating a basic Todo List application using React. The goal is to provide a beginner-friendly guide, breaking down each part of the code to help you understand how the application works.
Prerequisites
Before diving into the code, make sure you have a basic understanding of React. If you're new to React, consider going through the official React documentation and setting up a React development environment.
Setting Up Your React App
For this tutorial, we'll use Vite, a fast React development environment. If you haven't installed Vite, you can do so by running the following commands:
npm create vite@latest
External Dependencies
In addition to React, we'll use React Icons for the edit and delete icons in our Todo List. Install it using the following command:
npm i react-icons
Creating the Todo Component
import { useState } from "react";
import "./Todo.css";
import { FaRegEdit } from "react-icons/fa";
import { MdDelete } from "react-icons/md";
const Todo = () => {
const initialData = {
title: "",
date: "",
};
const [formData, setFormData] = useState(initialData);
const [todos, setTodos] = useState([]);
const [editMode, setEditMode] = useState(false);
const [editTodoId, setEditTodoId] = useState(null);
const handleChange = (e) => {
const { name, value } = e.target;
setFormData((prevFormData) => ({ ...prevFormData, [name]: value }));
};
const handleEdit = (id) => {
setEditMode(true);
const foundTodo = findTodoById(id);
if (foundTodo) {
setFormData(foundTodo);
setEditTodoId(foundTodo.id);
} else {
alert("Todo not found");
resetEditForm();
}
};
const handleSaveEdit = () => {
const updatedTodos = todos.map((todo) =>
todo.id === editTodoId ? formData : todo
);
setTodos(updatedTodos);
exitEditMode();
setFormData(initialData);
};
const handleCancelEdit = () => {
exitEditMode();
resetEditForm();
};
const handleDelete = (id) => {
const newTodos = todos.filter((todo) => todo.id !== id);
setTodos(newTodos);
};
const handleSubmit = (e) => {
if (editMode) {
handleSaveEdit();
return;
}
if (formData.title !== "" && formData.date !== "") {
e.preventDefault();
const newTask = createNewTask();
setTodos([...todos, newTask]);
resetEditForm();
} else {
alert("Please enter title and date");
}
};
const exitEditMode = () => {
setEditMode(false);
setEditTodoId(null);
};
const resetEditForm = () => {
setFormData(initialData);
exitEditMode();
};
const findTodoById = (id) => todos.find((todo) => todo.id === id);
const createNewTask = () => ({
id: Math.floor(Math.random() * (100 - 1) + 1),
title: formData.title,
date: formData.date,
});
return (
<div className="todoMainDiv">
<h1 className="todoHeading">Todo List</h1>
<div className="todo">
<input
name="title"
type="text"
placeholder="Title"
value={formData.title}
onChange={handleChange}
/>
<input
name="date"
type="date"
placeholder="Date"
value={formData.date}
onChange={handleChange}
/>
<button onClick={handleSubmit}>{editMode ? "Update" : "Save"}</button>
{editMode && <button onClick={handleCancelEdit}>Cancel</button>}{" "}
</div>
<div className="todoStatsTable">
<table>
<thead>
<tr>
<th>Title</th>
<th>Date</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{todos.map((todo, index) => (
<tr key={index}>
<td>{todo.title}</td>
<td>{todo.date}</td>
<td className="actions">
<FaRegEdit
className="actionBtn"
onClick={() => handleEdit(todo.id)}
/>
<MdDelete
className="actionBtn"
onClick={() => handleDelete(todo.id)}
/>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
};
export default Todo;
Import Necessary Dependencies and Styles
import { useState } from "react";
import "./Todo.css";
import { FaRegEdit } from "react-icons/fa";
import { MdDelete } from "react-icons/md";
FaRegEdit
and MdDelete
: These are components from the react-icons
library, providing icons for the edit and delete actions.
State Variables using the useState Hook
const initialData = {
title: "",
date: "",
};
const [formData, setFormData] = useState(initialData);
const [todos, setTodos] = useState([]);
const [editMode, setEditMode] = useState(false);
const [editTodoId, setEditTodoId] = useState(null);
This is a React hook used for managing state in functional components. Here, we're using it to create state variables for the form data, the list of todos, edit mode, and the ID of the todo being edited.
Event Handlers
const handleChange = (e) => {
const { name, value } = e.target;
setFormData((prevFormData) => ({ ...prevFormData, [name]: value }));
};
const handleEdit = (id) => {
setEditMode(true);
const foundTodo = findTodoById(id);
if (foundTodo) {
setFormData(foundTodo);
setEditTodoId(foundTodo.id);
} else {
alert("Todo not found");
resetEditForm();
}
};
const handleSaveEdit = () => {
const updatedTodos = todos.map((todo) =>
todo.id === editTodoId ? formData : todo
);
setTodos(updatedTodos);
exitEditMode();
setFormData(initialData);
};
const handleCancelEdit = () => {
exitEditMode();
resetEditForm();
};
const handleDelete = (id) => {
const newTodos = todos.filter((todo) => todo.id !== id);
setTodos(newTodos);
};
const handleSubmit = (e) => {
if (editMode) {
handleSaveEdit();
return;
}
if (formData.title !== "" && formData.date !== "") {
e.preventDefault();
const newTask = createNewTask();
setTodos([...todos, newTask]);
resetEditForm();
} else {
alert("Please enter title and date");
}
};
handleChange
: Updates the form data as the user types into the input fields.handleEdit
: Enters edit mode, retrieves the todo details, and sets them in the form for editing.handleSaveEdit
: Saves the edited todo, exits edit mode, and resets the form data.handleCancelEdit
: Cancels the edit action, exits edit mode, and resets the form data.handleDelete
: Deletes a todo by filtering it out from the list.handleSubmit
: Submits the form, either creating a new todo or updating an existing one.
Helper Functions
const exitEditMode = () => {
setEditMode(false);
setEditTodoId(null);
};
const resetEditForm = () => {
setFormData(initialData);
exitEditMode();
};
const findTodoById = (id) => todos.find((todo) => todo.id === id);
const createNewTask = () => ({
id: Math.floor(Math.random() * (100 - 1) + 1),
title: formData.title,
date: formData.date,
});
exitEditMode
: Exits edit mode and resets the edit todo ID.resetEditForm
: Resets the edit form by setting form data to initial values and exiting edit mode.findTodoById
: Finds a todo in the list by its ID.createNewTask
: Generates a new task with a random ID using form data.
JSX Structure
return (
<div className="todoMainDiv">
<h1 className="todoHeading">Todo List</h1>
<div className="todo">
<input
name="title"
type="text"
placeholder="Title"
value={formData.title}
onChange={handleChange}
/>
<input
name="date"
type="date"
placeholder="Date"
value={formData.date}
onChange={handleChange}
/>
<button onClick={handleSubmit}>{editMode ? "Update" : "Save"}</button>
{editMode && <button onClick={handleCancelEdit}>Cancel</button>}{" "}
</div>
<div className="todoStatsTable">
<table>
<thead>
<tr>
<th>Title</th>
<th>Date</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{todos.map((todo, index) => (
<tr key={index}>
<td>{todo.title}</td>
<td>{todo.date}</td>
<td className="actions">
<FaRegEdit
className="actionBtn"
onClick={() => handleEdit(todo.id)}
/>
<MdDelete
className="actionBtn"
onClick={() => handleDelete(todo.id)}
/>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
The JSX structure represents the UI of the Todo component.
Input fields allow the user to enter a title and date.
Buttons trigger actions such as submitting the form, canceling the edit action, and performing edit/delete actions.
The table displays the list of todos, with buttons for edit and delete actions.
Importing Todo Component and Running the App
In this section, we'll integrate the Todo
component into the App.jsx
file and run the React application to see the Todo List in action.
import Todo from "./components/Todo";
const App = () => {
return <Todo />;
};
export default App;
npm run dev
Final Output
Explore the complete source code for a basic Todo List application developed with React.
I hope you're enjoying your journey into React development. ๐ If you have any questions or need assistance with the code, feel free to reach out. Don't hesitate to explore and customize the Todo List app to match your preferences. Coding is a creative process, so have fun experimenting with different features and styles!
Remember, every line of code you write is a step forward in your learning journey. Happy coding! ๐ป๐
Subscribe to my newsletter
Read articles from Farhan Masood directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Farhan Masood
Farhan Masood
As a software engineer, my journey in web development has been marked by a mastery of front-end and back-end technologies, coupled with a commitment to continuous learning and exploration of new tech stacks. Beyond the realms of web development, I've ventured into cloud computing, containerization, and database management, embracing a versatile problem-solving approach. Passionate about crafting innovative solutions, I thrive in collaborative environments, valuing diverse perspectives within dynamic teams. Navigating the evolving tech landscape, my vision is to be a lifelong learner and innovator, contributing to the transformative power of technology.