I'm new to React. I'm trying to make a todo app, but every time when I update state, (I want to change some bool values), it gets deleted. Please, can someone explain, what is going on in my code, why it's not deleted in function todoDoneParent
. Code:
TodoApp component
class TodoApp extends Component {
state = {
todos: [
{id: 1, body: 'First Todo', isDone: false},
{id: 2, body: 'Second Todo', isDone: false},
{id: 3, body: 'Third Todo', isDone: false}
]
}
todoDoneParent= (id) => {
let copyState = [...this.state.todos];
let todo = copyState.filter(todo => todo.id == id );
todo[0].isDone = true;
let todos = copyState.filter(todo => todo.id != id );
this.setState({ todos: todo, todos });
console.log('INSIDE todoDoneParent', this.state.todos);///////////////////////////////FIRST PRINT
}
render() {
console.log('INSIDE TodoApp.render()', this.state.todos); ///////////////////////////////SECOND PRINT
return (
<div className="allTodos">
<Todos allTodos={this.state.todos} todoDoneParent={this.todoDoneParent}></Todos>
</div>
);
}
}
Todos component
class Todos extends Component {
todoDone = (e) => {
this.props.todoDoneParent(e.target.id);
}
render(){
const todos= this.props.allTodos;
const list = todos.length ? (
todos.map(todo => {
if(todo.isDone === false){
return(<div className='todo' key={todo.id}>
<div className='todo-container'>
<div>
<p>{todo.body}</p>
</div>
<button className='btn' id={todo.id} onClick={this.todoDone}>
Done
</button>
</div>
</div>)
}
})
) : (
<div>
<p>No todos</p>
</div>
);
return(
<div className='content'>
{list}
</div>
);
}
}
Outputs (in Console)
When it's just rendered
INSIDE TodoApp.render()
(3) [{…}, {…}, {…}]
0: {id: 1, body: "First Todo", isDone: false}
1: {id: 2, body: "Second Todo", isDone: false}
2: {id: 3, body: "Third Todo", isDone: false}
length: 3
__proto__: Array(0)
When I clicked any button
INSIDE todoDoneParent
(3) [{…}, {…}, {…}]
0: {id: 1, body: "First Todo", isDone: false}
1: {id: 2, body: "Second Todo", isDone: false}
2: {id: 3, body: "Third Todo", isDone: true}
length: 3
__proto__: Array(0)
INSIDE TodoApp.render()
(2) [{…}, {…}]
0: {id: 1, body: "First Todo", isDone: false}
1: {id: 2, body: "Second Todo", isDone: false}
length: 2__proto__: Array(0)
this.setState({ todos: todo, todos });
just overwrites this.state.todos
with the todos
that filtered out the one you wanted to mark done. It removes it from the todos
array.
Another issue is a type comparison issue where the id
type of your todos is "number", but the id
field coming back from the onClick
event is type "string". ===
uses type equality, i.e. 5 === '5'
is false, but 5 == '5'
.
If I understand correctly that you simply want to toggle the isDone
property of a specific todo to true
then you should map the old todos
to the new todos
and update the specific todo when the id
matches.
TodoApp
todoDoneParent = (id) => {
this.setState((prevState) => ({
todos: prevState.todos.map((todo) =>
todo.id === id
? {
...todo,
isDone: true
}
: todo
)
}));
};
Todos
Convert the id
value back to a number type for the comparison in the parent component.
todoDone = (e) => {
const { id } = e.target;
this.props.todoDoneParent(Number(id));
}