Home > Back-end >  React Deleting checkbox item from list, strange behavior
React Deleting checkbox item from list, strange behavior

Time:06-25

Here is my component:

enter image description here

After clicking delete, the checkbox underneath it becomes checked, even though it was never clicked.

enter image description here

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>
  );
}

CodePudding user response:

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:

  • Use controlled components instead of uncontrolled components; components are generally more predictable and useable with state, since the view should flow from the state. (If you use controlled components, you can also avoid from having to resort to a ref). Both the text input and the checkboxes can be made controlled.
  • Use keys when returning from .map
  • Don't mutate state in React; task.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>
  );
}
  • Related