![photo of react](/assets/technotes/react.webp)
What is React?
React is a javascript library used for building user interfaces and web applications. It uses a component-based architecture allowing developers to create reusable UI components that can be combined to build complex interfaces.
Simple Todo List App
The tutorial described in this post is from Learn React With This One Project tutorial by WebDevSimplified.
Install Vite
- Run
npm create vite@latest .
(The.
(period) will create the project inside whatever folder you are currently in). - Use the arrow key to select the React framework.
- Choose Javascript language.
- Run
npm i
to install all dependencies. - Next, run
npm run dev
to start the application.
Manage Directories
Delete the following directories:
- public
- src/assets
Manage Files
Delete the following files:
- src/index.css
- src/app.css
Create the following files:
- src/NewTodoForm.jsx
- src/TodoList.jsx
- src/TodoItem.jsx
- src/styles.css
main.jsx
In the src/main.jsx file delete the import './index.css'
line.
App.jsx
Delete everything in the src/App.jsx file and add the following:
import { useEffect, useState } from "react";
import { NewTodoForm } from "./NewTodoForm";
import "./styles.css";
import { TodoList } from "./TodoList";
export default function App() {
const [todos, setTodos] = useState(() => {
const localValue = localStorage.getItem("ITEMS");
if (localValue == null) return [];
return JSON.parse(localValue);
});
useEffect(() => {
localStorage.setItem("ITEMS", JSON.stringify(todos));
}, [todos]);
function addTodo(title) {
setTodos((currentTodos) => {
return [
...currentTodos,
{ id: crypto.randomUUID(), title, completed: false },
];
});
}
function toggleTodo(id, completed) {
setTodos((currentTodos) => {
return currentTodos.map((todo) => {
if (todo.id === id) {
return { ...todo, completed };
}
return todo;
});
});
}
function deleteTodo(id) {
setTodos((currentTodos) => {
return currentTodos.filter((todo) => todo.id !== id);
});
}
return (
<>
<NewTodoForm onSubmit={addTodo} />
<h1 className="header">Todo List</h1>
<TodoList todos={todos} toggleTodo={toggleTodo} deleteTodo={deleteTodo} />
</>
);
}
This is a React functional component called App
that represents a simple Todo list application. Let's break down the key parts of this file:
Imports
import { useEffect, useState } from "react";
import { NewTodoForm } from "./NewTodoForm";
import "./styles.css";
import { TodoList } from "./TodoList";
- The
useEffect
anduseState
are React hooks.useEffect
is used for handling side effects in functional components, anduseState
is used for managing state. - The component imports two other components:
NewTodoForm
andTodoList
. - The
styles.css
file is imported for styling purposes.
Component Functions
export default function App() {
This declares a functional component named App
.
State Management
const [todos, setTodos] = useState(() => {
const localValue = localStorage.getItem("ITEMS");
if (localValue == null) return [];
return JSON.parse(localValue);
});
It initializes a state variable todos
using the useState
hook. The initial state is retrieved from the local storage. If no data is stored, an empty array is used as the default state.
Local Storage and useEffect
useEffect(() => {
localStorage.setItem("ITEMS", JSON.stringify(todos));
}, [todos]);
The useEffect
hook is used here. It watches for changes in the todos
state, and whenever it changes, it updates the local storage with the new value.
Todo Manipulation Functions
addTodo
: Adds a new todo item to the state.toggleTodo
: Toggles the completion status of a todo item.deleteTodo
: Deletes a todo item from the state.
Render
return (
<>
<NewTodoForm onSubmit={addTodo} />
<h1 className="header">Todo List</h1>
<TodoList todos={todos} toggleTodo={toggleTodo} deleteTodo={deleteTodo} />
</>
);
- The component returns JSX, which represents the structure of the rendered component.
- It renders
NewTodoForm
for adding new todos, an<h1>
element with the class "header," and theTodoList
component to display the list of todos.
In summary, this React component manages a Todo list. It uses local storage to persist the todos, and it provides functions to add, toggle completion, and delete todos. The component structure consists of a form for adding new todos, a title for the todo list, and a list of todos with checkboxes to toggle completion and buttons to delete todos.
NewTodoForm.jsx
Open src/NewTodoForm.jsx and add the following code:
import { useState } from "react";
export function NewTodoForm({ onSubmit }) {
const [newItem, setNewItem] = useState("");
function handleSubmit(e) {
e.preventDefault();
if (newItem === "") return;
onSubmit(newItem);
setNewItem("");
}
return (
<form onSubmit={handleSubmit} className="new-item-form">
<div className="form-row">
<label htmlFor="item">New Item</label>
<input
value={newItem}
onChange={(e) => setNewItem(e.target.value)}
type="text"
id="item"
/>
</div>
<button className="btn">Add</button>
</form>
);
}
This React file defines a functional component named NewTodoForm
that represents a form for adding new items to a todo list. Let's break down the key parts of this file:
Import Statement
import { useState } from "react";
It imports the useState
hook from the React library. The useState
hook is used for managing state in functional components.
NewTodoForm Functional Component Definition
export function NewTodoForm({ onSubmit }) {
This declares a functional component named NewTodoForm
. It takes an object with a property onSubmit
as a parameter. This onSubmit
function is expected to handle the submission of a new item.
NewTodoForm State Management
const [newItem, setNewItem] = useState("");
It initializes a state variable newItem
using the useState
hook. This state represents the value of the input field in the form.
Form Submission Handling
function handleSubmit(e) {
e.preventDefault();
if (newItem === "") return;
onSubmit(newItem);
setNewItem("");
}
- The
handleSubmit
function is triggered when the form is submitted. e.preventDefault()
prevents the default form submission behavior, which would cause a page reload.- It checks if
newItem
is not an empty string, and if not, it calls theonSubmit
function with the current value ofnewItem
. - After submission, it resets the
newItem
state to an empty string.
Render JSX
return (
<form onSubmit={handleSubmit} className="new-item-form">
<div className="form-row">
<label htmlFor="item">New Item</label>
<input
value={newItem}
onChange={(e) => setNewItem(e.target.value)}
type="text"
id="item"
/>
</div>
<button className="btn">Add</button>
</form>
);
- The component renders a form with an
onSubmit
handler set to thehandleSubmit
function. - Inside the form, there is a label and an input field for entering the new item's text.
- The
value
of the input field is bound to thenewItem
state, and theonChange
event updates thenewItem
state as the user types. - Finally, there is a submit button with the label "Add."
In summary, this component provides a form for adding new items to a todo list. It manages the state of the input field (newItem
) and triggers a callback (onSubmit
) when the form is submitted. The onSubmit
function is expected to handle the logic of adding the new item to the todo list.
TodoList.jsx
Open src/TodoList.jsx and add the following code:
import { TodoItem } from "./TodoItem";
export function TodoList({ todos, toggleTodo, deleteTodo }) {
return (
<ul className="list">
{todos.length === 0 && "No Todos"}
{todos.map((todo) => {
return (
<TodoItem
{...todo}
key={todo.id}
toggleTodo={toggleTodo}
deleteTodo={deleteTodo}
/>
);
})}
</ul>
);
}
This React file defines a functional component named TodoList
that represents a list of todo items. Let's break down the key parts of this file:
TodoItem Import Statement
import { TodoItem } from "./TodoItem";
It imports the TodoItem
component from another file. Each individual todo item is represented by the TodoItem
component.
TodoList Functional Component Definition
export function TodoList({ todos, toggleTodo, deleteTodo }) {
This declares a functional component named TodoList
. It takes an object with properties todos
, toggleTodo
, and deleteTodo
as parameters. These properties represent the list of todos, a function to toggle the completion status of a todo, and a function to delete a todo.
Rendering the Todo List
return (
<ul className="list">
{todos.length === 0 && "No Todos"}
{todos.map((todo) => {
return (
<TodoItem
{...todo}
key={todo.id}
toggleTodo={toggleTodo}
deleteTodo={deleteTodo}
/>
);
})}
</ul>
);
- The component returns JSX, representing the structure of the rendered component.
- It renders an unordered list (
<ul>
) with the class name "list." - It checks if the length of the
todos
array is 0. If true, it displays the text "No Todos." This is a simple conditional rendering to handle the case when there are no todos. - It then uses the
map
function to iterate over eachtodo
in thetodos
array and renders aTodoItem
component for each todo. - The spread operator (
{...todo}
) is used to pass all properties of thetodo
object as individual props to theTodoItem
component. - Each
TodoItem
component is assigned a uniquekey
prop using thetodo.id
to help React efficiently update and re-render the list when needed. - The
toggleTodo
anddeleteTodo
functions are also passed as props to eachTodoItem
component, allowing them to communicate with the parent component (TodoList
).
In summary, the TodoList
component is responsible for rendering a list of todo items. It maps over the todos
array and renders a TodoItem
component for each todo, passing the necessary data and functions as props. If there are no todos, it displays a message indicating that there are no todos. The actual rendering of each todo item is delegated to the TodoItem
component.
TodoItem.jsx
Open src/TodoItem.jsx and add the following code:
export function TodoItem({ completed, id, title, toggleTodo, deleteTodo }) {
return (
<li>
<label>
<input
type="checkbox"
checked={completed}
onChange={(e) => toggleTodo(id, e.target.checked)}
/>
{title}
</label>
<button onClick={() => deleteTodo(id)} className="btn btn-danger">
Delete
</button>
</li>
);
}
This React file defines a functional component named TodoItem
that represents an individual todo item in a todo list. Let's break down the key parts of this file:
TodoItem Functional Component Definition
export function TodoItem({ completed, id, title, toggleTodo, deleteTodo }) {
This declares a functional component named TodoItem
. It takes an object with properties completed
, id
, title
, toggleTodo
, and deleteTodo
as parameters. These properties represent the details of a todo item and functions to handle toggling completion status and deleting the todo.
Rendering the Todo Item
return (
<li>
<label>
<input
type="checkbox"
checked={completed}
onChange={(e) => toggleTodo(id, e.target.checked)}
/>
{title}
</label>
<button onClick={() => deleteTodo(id)} className="btn btn-danger">
Delete
</button>
</li>
);
- The component returns JSX, representing the structure of the rendered todo item.
- It renders a list item (
<li>
) containing the details of a todo item. - Inside the list item, there is a label element containing an input checkbox and the title of the todo.
- The checkbox is controlled by the
checked
prop, which is set to the value of thecompleted
property. TheonChange
event is used to handle changes to the checkbox. When the checkbox is toggled, thetoggleTodo
function is called with theid
of the todo and the new checked state. - The title of the todo is displayed inside the label.
- There is also a delete button (
<button>
) with anonClick
event. When the button is clicked, thedeleteTodo
function is called with theid
of the todo. The button has a class name for styling purposes.
In summary, the TodoItem
component is responsible for rendering the details of an individual todo item. It includes a checkbox to toggle the completion status and a delete button to remove the todo item. The component receives the todo details and callback functions (toggleTodo
and deleteTodo
) as props, making it reusable and allowing it to interact with the parent components that manage the todo list.