I've just started learning React and I'm trying to build a todo list including edit button. I defined an input inside
import "./index.css";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faDeleteLeft, faPenToSquare } from "@fortawesome/free-solid-svg-icons";
import { useState } from "react";
const todoItems = [
{ description: "Task 1", id: 111, done: false, edit: false },
{ description: "Task 2", id: 222, done: false, edit: false },
{ description: "Task 3", id: 333, done: false, edit: false },
];
export default function App() {
return (
<div
className="
app"
>
<ToDo />
</div>
);
}
function ToDo() {
const [tasks, setTasks] = useState(todoItems);
function handleAddTask(task) {
setTasks((tasks) => [...tasks, task]);
}
function handleToggleTask(id) {
setTasks(
tasks.map((task) =>
task.id === id ? { ...task, done: !task.done } : task
)
);
}
function handleDeleteTask(id) {
setTasks(tasks.filter((task) => task.id !== id));
}
function handleEditTask(id) {
setTasks(
tasks.map((task) =>
task.id === id ? { ...task, edit: !task.edit } : task
)
);
}
const handleSaveEdit = (id, newText) => {
setTasks(
tasks.map((task) =>
task.id === id ? { ...task, description: newText, edit: false } : task
)
);
};
return (
<div className="todo-container">
<Header />
<TodoList
tasks={tasks}
onToggleTask={handleToggleTask}
onDeleteTask={handleDeleteTask}
onEditTask={handleEditTask}
onSaveEdit={handleSaveEdit}
/>
<TodoForm onAddTask={handleAddTask} />
</div>
);
}
function Header() {
return (
<header className="todo-header">
<h2>Todo List</h2>
</header>
);
}
function TodoList({
tasks,
onToggleTask,
onDeleteTask,
onEditTask,
onSaveEdit,
}) {
return (
<ul className="todo-list">
{tasks.map((task) => (
<TodoItem
task={task}
onToggleTask={onToggleTask}
onDeleteTask={onDeleteTask}
onEditTask={onEditTask}
onSaveEdit={onSaveEdit}
/>
))}
</ul>
);
}
function TodoItem({
task,
onToggleTask,
onDeleteTask,
onEditTask,
onSaveEdit,
}) {
return (
<li className="todo-item" key={task.id}>
<input
type="checkbox"
value={task.done}
onChange={() => onToggleTask(task.id)}
/>
{task.edit ? (
<input
type="text"
value={task.description}
onChange={(e) => onSaveEdit(task.id, e.target.value)}
style={{ width: "100%", border: "none", outline: "none" }}
/>
) : (
<label className={task.done ? "done-task" : ""}>
{task.description}
</label>
)}
{!task.edit && (
<>
<FontAwesomeIcon
icon={faPenToSquare}
style={{ cursor: "pointer", margin: "0 10px" }}
onClick={() => onEditTask(task.id)}
/>
<FontAwesomeIcon
style={{ cursor: "pointer" }}
icon={faDeleteLeft}
rotation={180}
onClick={() => onDeleteTask(task.id)}
/>
</>
)}
</li>
);
}
function TodoForm({ onAddTask }) {
const [description, setDescription] = useState("");
function handleSubmit(e) {
e.preventDefault();
if (description.trim() === "") return;
const id = crypto.randomUUID();
const newTask = { description, id, done: false, edit: false };
console.log(newTask);
onAddTask(newTask);
setDescription("");
}
return (
<form className="todo-input" onSubmit={(e) => handleSubmit(e)}>
<input
type="text"
placeholder="Add a new task"
value={description}
onChange={(e) => setDescription(e.target.value)}
/>
<button>Add</button>
</form>
);
}
I've searched but i could not find the same case as mine! Can anybody help me on this?
That's exactly what you do on this line:
onChange={(e) => onSaveEdit(task.id, e.target.value)}
Which calls this handler, setting edit: false
:
const handleSaveEdit = (id, newText) => {
setTasks(
tasks.map((task) =>
task.id === id ? { ...task, description: newText, edit: false } : task
)
);
};
You have two options:
Use an uncontrolled input with a "save" button on the side. Once the user is done editing that value, they can click the "save" button, which calls handleSaveEdit
. newText
can be obtained by adding a ref
to the <input>
. Additionally, you might want to have a "cancel" / "discard change" button.
Keep using a controlled input and automatically "save" the value as it changes. In this case, you just need a different handler that updates the target task's description
without changing its edit
property:
const handleEdit = (id, newText) => {
setTasks(
tasks.map((task) =>
task.id === id ? { ...task, description: newText } : task
)
);
};