Here is my component:
After clicking delete, the checkbox underneath it becomes checked, even though it was never clicked.
Here is the parent and child component implementations (new to React)
TaskList.js
import { useState, useRef } from "react";
import Task from "./Task";
function TaskList({ className, tasks, setTasks }) {
let inputRef = useRef();
return (
<div className={className}>
<ul className="grow overflow-y-scroll">
{tasks.map((task, i) => (
<Task
description={task.description}
onChange={() => {
task.checked = !task.checked;
}}
/>
))}
</ul>
<input
className="my-2 border-b-2 border-blue-300"
placeholder="Enter task description..."
ref={inputRef}
></input>
<button
className="rounded-md bg-blue-400 w-full p-4 text-white text-2xl"
onClick={() => {
setTasks([
...tasks,
{ description: inputRef.current.value, checked: false },
]);
}}
>
Add
</button>
<button
className="w-full bg-red-600 rounded-md p-2"
onClick={() => {
setTasks(tasks.filter((t) => !t.checked));
}}
>
Delete
</button>
</div>
);
}
export default TaskList;
Task.js
import React, { useState } from "react";
function Task({ description, onChange }) {
return (
<li className="flex flex-row my-2">
<input className="mx-2" type="checkbox" onChange={onChange}></input>
<span>{description}</span>
</li>
);
}
You're missing keys from the Task component, and you're using uncontrolled components, so when there's a re-render, you aren't telling React enough about which element that exists in the DOM corresponds to which <Task>. The best fix would be to:
.maptask.checked = !task.checked; should be refactored.Something along the lines of:
function TaskList({ className, tasks, setTasks }) {
const [value, setValue] = useState(''); // <-------------------
return (
<div className={className}>
<ul className="grow overflow-y-scroll">
{tasks.map((task, i) => (
<Task
key={task.description} // <-------------------
description={task.description}
checked={task.checked} // <-------------------
onChange={() => {
setTasks( // <-------------------
tasks.map(
(task, j) => i !== j ? task : { ...task, checked: !task.checked }
)
);
}}
/>
))}
</ul>
<input
className="my-2 border-b-2 border-blue-300"
placeholder="Enter task description..."
value={value} // <-------------------
onChange={(e) => setValue(e.currentTarget.value)} // <-------------------
></input>
<button
className="rounded-md bg-blue-400 w-full p-4 text-white text-2xl"
onClick={() => {
setTasks([
...tasks,
{ description: value, checked: false }, // <-------------------
]);
}}
>
...
function Task({ description, onChange, checked }) { // <-------------------
return (
<li className="flex flex-row my-2">
<input className="mx-2" type="checkbox" onChange={onChange} checked={checked} /> // <-------------------
<span>{description}</span>
</li>
);
}