Reputation: 165
Currently, it's giving me an error saying that isCompleted is undefined.
todo.isCompleted = todo.isCompleted ? false : true;
The above code is what's throwing the error.
The advice Im getting is to pass a todoIndex prop in App.js when you're rendering the Todo component.
Not sure how to go about doing that. Any pointers?
import React, { Component } from 'react';
import './App.css';
import ToDo from './components/ToDo.js';
class App extends Component {
constructor(props) {
super(props);
this.state = {
todos: [
{ description: 'Walk the cat', isCompleted: true },
{ description: 'Throw the dishes away', isCompleted: false },
{ description: 'Buy new dishes', isCompleted: false }],
newTodoDescription: ''
};
}
deleteToDo(deleteToDo) {
console.log(this);
let newToDos = this.state.todos.filter((todo) => {
return todo !== deleteToDo
} )
this.setState({ todos: newToDos });
}
handleChange(e) {
this.setState({ newTodoDescription: e.target.value })
}
handleSubmit(e) {
e.preventDefault();
if (!this.state.newTodoDescription) { return }
const newTodo = { description: this.state.newTodoDescription, isCompleted: false };
this.setState({ todos: [...this.state.todos, newTodo], newTodoDescription: '' });
}
toggleComplete(index) {
const todos = this.state.todos.slice();
const todo = todos[index];
todo.isCompleted = todo.isCompleted ? false : true;
this.setState({ todos: todos });
}
render() {
return (
<div className="App">
<ul>
{ this.state.todos.map( (todo, index) =>
<ToDo key={ index } description={ todo.description } isCompleted={ todo.isCompleted } toggleComplete={ this.toggleComplete.bind(this) } deleteToDo={() => this.deleteToDo(todo)} />
)}
</ul>
<form onSubmit={ (e) => this.handleSubmit(e) }>
<input type="text" value={ this.state.newTodoDescription } onChange={ (e) => this.handleChange(e) } />
<input type="submit" />
</form>
</div>
);
}
}
export default App;
ToDo.js
import React, { Component } from 'react';
class ToDo extends Component {
toggleComplete = () => {
this.props.toggleComplete(this.props.todoIndex)
}
render() {
return (
<ul>
<input type= "checkbox" checked= { this.props.isCompleted }
onChange= { this.handleToggleClick.bind(this)} />
<span>{ this.props.description }</span>
<button onClick={ this.props.deleteToDo }> X </button>
</ul>
);
}
}
export default ToDo;
Upvotes: 0
Views: 214
Reputation: 31
You're not passing the index to ToDo, so toggleComplete doesn't know which todo to update.
Fix in App.js
Pass index as a prop:
<ToDo
key={index}
description={todo.description}
isCompleted={todo.isCompleted}
toggleComplete={() => this.toggleComplete(index)} // Pass index
deleteToDo={() => this.deleteToDo(todo)}
/>
Fix in ToDo.js
Update toggleComplete to call the prop directly:
<input
type="checkbox"
checked={this.props.isCompleted}
onChange={this.props.toggleComplete} // No need for another function
/>
Upvotes: 0
Reputation: 675
To pass the index to the ToDo
component add another prop:
<ToDo key={index} todoIndex={index} ... />
and make sure that the component calls toggleComplete
with that index prop, i.e.
class ToDo extends React.Component {
toggleComplete() {
this.props.toggleComplete(this.props.todoIndex)
}
}
Also, you're mutating the todo object in your toggleComplete
function, instead of:
todo.isCompleted = todo.isCompleted ? false : true;
Better do this:
const todos[index] = {...todo, isCompleted: !todo.isCompleted }
Or with Object.assign
:
const todos[index] = Object.assign({}, todo, {isCompleted: !isCompleted})
I think Max Kurtz answer is also correct, the this
binding seems to be problematic. Bind the toggleComplete
function in the constructor or use arrow functions to make sure this
doesn't bite you.
Upvotes: 2