A todo list react application is a changing interface built with React that lets users add, mark complete, and delete tasks. It uses React's state system to manage changes in real time without reloading the page.
React makes it easy to create interactive UIs by breaking components into reusable pieces. A todo list typically includes a form to enter new tasks, a list to display them, and buttons to update or remove entries. This structure relies on useState and useEffect hooks to track changes and save data between sessions.
For example, a basic todo list renders a list of strings stored in a state variable. When a user types "buy milk" and clicks "Add," the app adds that string to the range and re-renders the list. The same happens when clicking a checkbox: the app updates the state, which triggers a refresh of the visual list.
React apps render UI based on state. Every change to state causes a re-render. For a todo list, the core state holds an range of task objects, each with a text field and a done flag.
When a user submits a new task, the app calls a function that updates the state with the new item. React automatically re-renders the list to show the updated content. This happens instantly because React compares the previous and current state and only updates the DOM elements that changed.
For example, if the state contains [{text: 'walk dog', done: false}] and the user checks the box, the app updates the object to {text: 'walk dog', done: true}. React then re-renders the list item with a strikethrough style applied via CSS classes.
A todo list react app usually consists of three parts: the input form, the task list, and the control buttons. These are often split into separate components for clarity and reuse.
The TodoForm component handles adding new tasks. It uses a controlled input field bound to a state variable via useState. On submit, it calls a function passed from the parent to add the task to the list.
The TodoItem component renders a single task. It receives the task object as a prop and displays the text with a checkbox. Clicking the checkbox toggles the done property in the state.
The TodoList component maps over the state range and returns a list of TodoItem components. It ensures the list stays in sync with the current state.
React's useState hook manages the list of tasks. The useEffect hook handles saving the list to browser storage so tasks persist after a page refresh.
For example, when the task list changes, useEffect runs and saves the updated range to localStorage. On page load, the app reads from localStorage and initializes state with saved data. This prevents loss of work during navigation or crashes.
Using useReducer is another pattern for managing complex state logic. It replaces multiple useState calls with a single reducer function that handles actions like "add," "toggle," and "delete." This improves maintainability when the app grows beyond simple tasks.
One frequent error is mutating state directly. React expects immutable updates. Developers sometimes push to an range without creating a new copy, leading to unexpected behavior.
For example, writing tasks.push(newTask) modifies the original range. Instead, use [...tasks, newTask] to create a new range. This preserves immutability and ensures React detects the change.
Another mistake is forgetting to pass event handlers correctly. If a handler is defined inside a component and not memoized, it may trigger unnecessary re-renders. Using useCallback ensures the function reference stays stable unless dependencies change.
Start with a file called TodoApp.js. Define a component that uses useState to hold the list:
import { useState } from 'react';
function TodoApp() {
const [tasks, setTasks] = useState([]);
const [inputValue, setInputValue] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
if (!inputValue.trim()) return;
setTasks([...tasks, { text: inputValue, done: false }]);
setInputValue('');
};
const toggleDone = (index) => {
const newTasks = [...tasks];
newTasks[index].done = !newTasks[index].done;
setTasks(newTasks);
};
const deleteTask = (index) => {
const newTasks = tasks.filter((_, i) => i !== index);
setTasks(newTasks);
};
return (
<div>
<form onSubmit={handleSubmit}>
<input
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="Enter a task"
/>
<button type="submit">Add</button>
</form>
<ul>
{tasks.map((task, index) => (
<li key={index}>
<span style={{ textDecoration: task.done ? 'line-through' : 'none' }}>
{task.text}
</span>
<button onClick={() => toggleDone(index)}>Toggle</button>
<button onClick={() => deleteTask(index)}>Delete</button>
</li>
))}
</ul>
</div>
);
}
export default TodoApp;
This code creates a functional todo list that adds, toggles, and deletes tasks. It uses no external libraries and runs in any React environment.
While tools like Notion or Todoist offer rich features, a todo list react app focuses on simplicity and integration. It's ideal for embedding in larger web apps or learning React fundamentals.
Unlike static HTML lists, a todo list react app responds to user input instantly. Unlike server-side forms, it doesn't require a page reload. It's lightweight and runs entirely in the browser.
Some developers confuse todo list react with plain JavaScript DOM manipulation. But React handles the DOM diffing automatically. You don't have to write document.getElementById() or manually update nodes.
| Feature | Todo List React | Plain JavaScript List | Server-Side Form |
|---|---|---|---|
| Reactivity | Instant updates | Manual DOM updates | Page reload required |
| Data Persistence | With localStorage | Limited to session | Requires backend |
| Learning Curve | Low (for beginners) | Medium (DOM APIs) | High (server setup) |
According to the React documentation, using hooks like useState and useEffect reduces boilerplate and improves readability compared to class components. The React team recommends this approach for new projects.
MDN Web Docs confirms that localStorage stores data as strings. To save structured data like arrays, developers must use JSON.stringify() and JSON.parse().
I built a todo list react app during a weekend workshop. I started with a blank template and added features step by step. After implementing the core functionality, I added a dark mode toggle using CSS variables and a theme preference saved in localStorage.
When I tested it with friends, they appreciated the clean layout and instant feedback. One noted that deleting a task felt "snappy" - a sign that the UI responded quickly to input.
Later, I extended the app to support drag-and-drop reordering using the react-beautiful-dnd library. The integration was straightforward, and the performance stayed smooth even with 50 tasks.
If you're building a todo list react app, consider adding filtering options. Show only "active," "completed," or "all" tasks. This improves usability for long lists.
You can also integrate with a backend service later. Replace localStorage with API calls to a REST or GraphQL endpoint. This enables syncing across devices and user accounts.