React is a popular JavaScript library for building dynamic user interfaces. It allows developers to create reusable components and manage the state of their applications efficiently. One of the common projects to get started with React is building a Todo app. A Todo app helps users keep track of tasks, allowing them to add, delete, and mark tasks as complete.
In this step-by-step guide, we will build a Todo app using React. We will start by setting up a new React project and creating the necessary components. We will manage the state of our application using React’s useState
hook and add functionality to add, delete, and toggle todos. Additionally, we will style our app and add local storage to persist todos across sessions.
Setting Up the React Project
Creating a New React App
To get started, we need to create a new React app using Create React App, which sets up a new React project with a standard structure and configuration.
Run the following command to create a new React app:
npx create-react-app todo-app
cd todo-app
This command sets up a new React application in a directory named todo-app
.
Installing Required Packages
For our Todo app, we will use some basic React packages. At this point, we don’t need any additional packages beyond what Create React App provides.
Creating the Todo Components
Creating the Todo List Component
First, we will create a component that displays the list of todos. Create a components
directory in the src
folder, and then create a file named TodoList.js
.
// src/components/TodoList.js
import React from 'react';
import TodoItem from './TodoItem';
const TodoList = ({ todos, toggleTodo, deleteTodo }) => {
return (
<ul>
{todos.map((todo) => (
<TodoItem key={todo.id} todo={todo} toggleTodo={toggleTodo} deleteTodo={deleteTodo} />
))}
</ul>
);
};
export default TodoList;
In this component, we map over the list of todos and render a TodoItem
component for each todo. We pass the toggleTodo
and deleteTodo
functions as props to handle interactions with each todo.
Creating the Todo Item Component
Next, we create a component that represents a single todo item. Create a file named TodoItem.js
in the components
directory.
// src/components/TodoItem.js
import React from 'react';
const TodoItem = ({ todo, toggleTodo, deleteTodo }) => {
return (
<li style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
<span onClick={() => toggleTodo(todo.id)}>{todo.text}</span>
<button onClick={() => deleteTodo(todo.id)}>Delete</button>
</li>
);
};
export default TodoItem;
In this component, we display the text of the todo and apply a line-through style if the todo is completed. We also add buttons to toggle the completion status and delete the todo.
Creating the Add Todo Component
We need a component to add new todos. Create a file named AddTodo.js
in the components
directory.
// src/components/AddTodo.js
import React, { useState } from 'react';
const AddTodo = ({ addTodo }) => {
const [text, setText] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
if (text.trim()) {
addTodo(text);
setText('');
}
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="Add a new todo"
/>
<button type="submit">Add</button>
</form>
);
};
export default AddTodo;
In this component, we manage the input state using useState
. When the form is submitted, we call the addTodo
function passed as a prop and reset the input field.
Managing State in the Todo App
Using useState for State Management
Now that we have our components, we need to manage the state of our todos in the main App
component. Open src/App.js
and update it as follows:
// src/App.js
import React, { useState, useEffect } from 'react';
import TodoList from './components/TodoList';
import AddTodo from './components/AddTodo';
const App = () => {
const [todos, setTodos] = useState([]);
const addTodo = (text) => {
const newTodo = { id: Date.now(), text, completed: false };
setTodos([...todos, newTodo]);
};
const toggleTodo = (id) => {
setTodos(
todos.map((todo) =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
);
};
const deleteTodo = (id) => {
setTodos(todos.filter((todo) => todo.id !== id));
};
return (
<div>
<h1>Todo App</h1>
<AddTodo addTodo={addTodo} />
<TodoList todos={todos} toggleTodo={toggleTodo} deleteTodo={deleteTodo} />
</div>
);
};
export default App;
In this code, we define the todos
state using useState
. We also define the addTodo
, toggleTodo
, and deleteTodo
functions to manage the state of our todos. These functions are passed as props to the respective components.
Styling the Todo App
Basic CSS Styling
To style our Todo app, create a styles.css
file in the src
directory and add some basic styles:
/* src/styles.css */
body {
font-family: Arial, sans-serif;
}
h1 {
text-align: center;
}
form {
display: flex;
justify-content: center;
margin-bottom: 20px;
}
input {
padding: 10px;
font-size: 16px;
margin-right: 10px;
}
button {
padding: 10px;
font-size: 16px;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: flex;
justify-content: space-between;
padding: 10px;
background: #f9f9f9;
border-bottom: 1px solid #ddd;
margin-bottom: 10px;
}
li span {
cursor: pointer;
}
li button {
background: #ff6347;
border: none;
padding: 5px 10px;
color: white;
cursor: pointer;
}
Import the CSS file in src/index.js
:
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import './styles.css';
import App from './App';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
These styles provide a basic layout and design for our Todo app, making it more visually appealing.
Conditional Styling for Completed Todos
We have already added conditional styling for completed todos in the TodoItem
component by applying a line-through style when a todo is completed.
// src/components/TodoItem.js
<li style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
<span onClick={() => toggleTodo(todo.id)}>{todo.text}</span>
<button onClick={() => deleteTodo(todo.id)}>Delete</button>
</li>
This ensures that completed todos are visually distinct from incomplete ones.
Adding Local Storage
Persisting Todos in Local Storage
To persist the todos in local storage, we need to save the todos state whenever it changes and load the todos from local storage when the component mounts.
Update the App
component:
// src/App.js
import React, { useState, useEffect } from 'react';
import TodoList from './components/TodoList';
import AddTodo from './components/AddTodo';
const App = () => {
const [todos, setTodos] = useState(() => {
const savedTodos = localStorage.getItem('todos');
return savedTodos ? JSON.parse(savedTodos) : [];
});
useEffect(() => {
localStorage.setItem('todos', JSON.stringify(todos));
}, [todos]);
const addTodo = (text) => {
const newTodo = { id: Date.now(), text, completed: false };
setTodos([...todos, newTodo]);
};
const toggleTodo = (id) => {
setTodos(
todos.map((todo) =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
);
};
const deleteTodo = (id) => {
setTodos(todos.filter((todo) => todo.id !== id));
};
return (
<div>
<h1>Todo App</h1>
<AddTodo addTodo={addTodo} />
<TodoList todos={todos} toggleTodo={toggleTodo} deleteTodo={deleteTodo} />
</div>
);
};
export default App;
In this code, we initialize the todos
state with the saved todos from local storage (if any) using the useState
hook. We also use the useEffect
hook to save the todos to local storage whenever they change.
Conclusion
Building a Todo app with React is a great way to learn the fundamentals of React development. By creating reusable components, managing state, and adding functionality, you can build a fully functional Todo app. We covered setting up the project, creating components, managing state, styling the app, and persisting data in local storage. These concepts provide a solid foundation for building more complex applications with React.
Additional Resources
To further your understanding of building Todo apps with React, here are some valuable resources:
- React Documentation: The official React documentation provides comprehensive information on React features and best practices. React Documentation
- React Hooks Documentation: Learn more about React hooks, including
useState
anduseEffect
. React Hooks Documentation - CSS-Tricks: A great resource for learning CSS and styling techniques. CSS-Tricks
- MDN Web Docs: Comprehensive documentation on web technologies, including JavaScript and DOM manipulation. MDN Web Docs
- Udemy: Udemy offers extensive courses on React development for all levels. Udemy React Courses
By leveraging these resources, you can deepen your understanding of React development and enhance your web development skills.