Doge
Doge

Reputation: 81

checked checkbox remain after re-rendering

i'm building a checkbox todo List that checked checkbox is disappearing.

I have two problems.

  1. checked checkbox remains after re-rendering
  2. When the two checkboxes are left, they dont disappear.

Here is my codeSandBox:-----

I think this might be a setState issue, setItems([...items.filter((item) => !checkedItems[item.id])]); -> rerendering (this scope havecheckedItems ={false,true,false,false,false}) so, checkbox is remaining?

import "./styles.css";
import React from "react";

const todos = [
  { id: 0, value: "Wash the dishes" },
  { id: 1, value: "have lunch" },
  { id: 2, value: "listen to music" },
  { id: 3, value: "running" },
  { id: 4, value: "work out" }
];
export default function App() {
  const [items, setItems] = React.useState(todos);
  const [checkedItems, setCheckedItems] = React.useState(
    new Array(todos.length).fill(false)
  );

  const checkHandler = (idx) => {
    checkedItems[idx] = !checkedItems[idx];
    setItems([...items.filter((item) => !checkedItems[item.id])]);
    setCheckedItems([...checkedItems]);
  };
  return (
    <div className="App">
      {items.map((todo, idx) => (
        <div key={idx}>
          <span>{todo.value}</span>
          <input
            type="checkbox"
            checked={checkedItems[idx]}
            onChange={() => checkHandler(idx)}
          ></input>
        </div>
      ))}
    </div>
  );
}

Upvotes: 0

Views: 145

Answers (4)

Hassan Ahmed
Hassan Ahmed

Reputation: 114

The key mistake you're making here is in onChange={() => checkHandler(idx)} and then using idx as your variable to filter out your items. idx does NOT equal the ids you have in your todo list, it will change based on the number of items left in the array.

The filter can also be improved to just be

setItems([...items.filter((item) => item.id !== idx)]);

The final code should look something like this (I'm not sure what checkedItems is meant to be doing or what it's for so it's not a consideration in this answer).

import "./styles.css";
import React from "react";

const todos = [
  { id: 0, value: "Wash the dishes" },
  { id: 1, value: "have lunch" },
  { id: 2, value: "listen to music" },
  { id: 3, value: "running" },
  { id: 4, value: "work out" }
];
export default function App() {
  const [items, setItems] = React.useState(todos);
  const [checkedItems, setCheckedItems] = React.useState(
    new Array(todos.length).fill(false)
  );

  const checkHandler = (idx) => {
    checkedItems[idx] = !checkedItems[idx];
    setItems([...items.filter((item) => item.id !== idx)]);
    setCheckedItems([...checkedItems]);
  };
  return (
    <div className="App">
      {items.map((todo, idx) => (
        <div key={idx}>
          <span>{todo.value}</span>
          <input
            type="checkbox"
            onChange={() => checkHandler(todo.id)}
          ></input>
        </div>
      ))}
    </div>
  );
}

Upvotes: 1

Apostolos
Apostolos

Reputation: 10463

It is not good practice to use index as key when iterating objects. Since you have id, use this.

  {items.map(todo => (
    <div key={todo.id}>
      <span>{todo.value}</span>
      <input
        type="checkbox"
        checked={checkedItems[todo.id]}
        onChange={() => checkHandler(todo.id)}
      ></input>
    </div>
  ))}

You mess with indexes and the result is confusing. If you want the items to be persisted and shown, just remove this line

setItems([...items.filter((item) => !checkedItems[item.id])]);
    

Demo

Upvotes: 1

Muhammad Wasif
Muhammad Wasif

Reputation: 84

You do not need to have a separate checkedItems state. You can add a field checked in your todo object.

const todos = [
  { id: 0, value: "Wash the dishes", checked: false },
  { id: 1, value: "have lunch", checked: false },
  { id: 2, value: "listen to music", checked: false },
  { id: 3, value: "running", checked: false },
  { id: 4, value: "work out", checked: false }
];
export default function App() {
  const [items, setItems] = React.useState(todos);


  const checkHandler = (idx) => {
    setItems(items.filter((item) => item.id !== idx));
  };
  return (
    <div className="App">
      {items.map((todo, idx) => (
        <div key={idx}>
          <span>{todo.value}</span>
          <input
            type="checkbox"
            checked={todo.checked}
            onChange={() => checkHandler(todo.id)}
          ></input>
        </div>
      ))}
    </div>
  );
}

Upvotes: 1

Ahmet Emre Kilinc
Ahmet Emre Kilinc

Reputation: 6915

Simply remove this line:

setItems([...items.filter((item) => !checkedItems[item.id])]);

that causes the list items to be filtered.

Upvotes: 0

Related Questions