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;
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.