Reputation: 81
i'm building a checkbox todo List that checked checkbox is disappearing.
I have two problems.
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
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
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])]);
Upvotes: 1
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
Reputation: 6915
Simply remove this line:
setItems([...items.filter((item) => !checkedItems[item.id])]);
that causes the list items to be filtered.
Upvotes: 0