skip to Main Content

I am trying to do a list with a checkbox in front of it using reactJS. It is checking all the checkboxes when you click on a single checkbox, while it was suppose to just check the single checkbox that was clicked. How do I fix it?

const checkHandler = () => {
  if (checked) {
    setChecked(false);
  }

  if (!checked) {
    setChecked(true);
  }
};
return (
  <ul>
    {props.taskInputs.map((e) => (
      <li key={e.id}>
        {e.id && !checked && (
          <ImCheckboxChecked className="checkbox" onClick={checkHandler} />
        )}
        {e.id && checked && (
          <ImCheckboxUnchecked className="checkbox" onClick={checkHandler} />
        )}

        <span
          // className='tasks'
          className={checked ? "tasks" : "tasksDone"}
        >
          {e.taskInput}
        </span>
        <LuEdit
          className="edit-icon"
          onClick={() => props.editButtonHandler(e.id, e.taskInput)}
        />
        <BsTrash
          className="delete-icon"
          onClick={() => props.removeButtonHandler(e.id)}
        />
      </li>
    ))}
  </ul>
)

3

Answers


  1. It is because you are using the same state variable "checked" for all the checkboxes. You need to have a separate one for each checkbox.

    Login or Signup to reply.
  2. The problem here is that all the checkboxes share to the same checked state variable.

    you can create a copy of taskInputs in the child component adding for each object a property isChecked equal to false and store them in a local state taskInputsWithCheck:

    const [taskInputsWithCheck, setTaskInputsWithCheck] = useState([])
    //...
    useEffect(() => {
      setTaskInputsWithCheck(
      props.taskInputs.map((input) => {
        return { ...input, isChecked: false };
      })
    );
    
    },[props.taskInputs])
    

    then use this function when you click to toggle an input:

    const toggleInput = (id) => {
      setTaskInputsWithCheck((prev) =>
        prev.map((input) => {
          if (input.id === id) {
            return { ...input, isChecked: !input.isChecked };
          } else {
            return input;
          }
        })
      );
    }
    
    onClick={() => {
     toggleInput(e.id)
    }}
    

    don’t forget to map through taskInputsWithCheck inside your JSX instead of props.taskInputs and to use e.isChecked instead of checked state

    one other solution is to add isChecked property directly to each object of taskInputs, then from the child component you use it and when you click to check/uncheck it from the child component, you have to update taskInputs state in the parent component.
    to do so, you can follow the same logic, create the function in the parent component and pass it to child as props.

    Login or Signup to reply.
  3. As bored-coder stated, you need to track the state of each of your checkboxes individually.

    That doesn’t mean you need N useStates, you could use a Record to keep track of all the states using a single useState.

    const Example = ({ editButtonHandler, removeButtonHandler, taskInputs, ...remainingProps }) => {
        // used to hold check state of all checkboxes
        // In Typescript this would be a Record<number, boolean> (assuming id is a number).
        const [checked, setChecked] = useState({});
    
        // handler for checkbox event
        const checkHandler = (id) => {
            setChecked(current => {
                // if id key exists and is true,
                // set to false
                if (current[id]) {
                    current[id] = false;
                }
                // if id key does not exist or is false,
                // set to true
                else {
                    current[id] = true;
                }
            })
        }
    
        return (
            <ul>
                {
                    taskInputs.map( (e) => (
                        <li 
                            key={e.id}
                        >
                            {
                                e.id && !current[id] && (
                                    <ImCheckboxChecked 
                                        className='checkbox' 
                                        onClick={() => {
                                            // pass in ID to checkHandler
                                            checkHandler(e.id)
                                        }}
                                    />
                                )
                            }
                            {
                                e.id && current[id] && (
                                    <ImCheckboxUnchecked 
                                        className='checkbox' 
                                        onClick={() => {
                                            // pass in ID to checkHandler
                                            checkHandler(e.id)
                                        }}
                                    />
                                )
                            }
                            <span 
                                className={checked ? 'tasks' : 'tasksDone'}
                            >
                                {e.taskInput}
                            </span>
                            <LuEdit 
                                className="edit-icon" 
                                onClick={() => editButtonHandler(e.id, e.taskInput)}
                            />
                            <BsTrash 
                                className="delete-icon" 
                                onClick={() => removeButtonHandler(e.id)}
                            />
                        </li>
                    ))
                }
            </ul>
        )
    };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search