Reputation: 853
App.js (parent component)
import React from "react";
import TodoItem from "./TodoItem";
import todosData from "./todosData";
import "./styles.css";
class App extends React.Component {
constructor() {
super();
this.state = {
todos: todosData
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(id) {
console.log(`${id} Clicked!`);
}
render() {
const todoItems = this.state.todos.map(function (item) {
return (
<TodoItem
key={item.id}
text={item.text}
completed={item.completed}
item={item}
handleChange={this.handleChange}
/>
);
});
return <div className="todo-list">{todoItems}</div>;
}
}
export default App;
TodoItem.js (child component)
import React from "react";
function TodoItem(props) {
return (
<div className="todo-item">
<input
type="checkbox"
checked={props.completed}
onChange={() => props.handleChange(props.item.id)}//PROBLEM LINE
/>
<p>{props.text}</p>
</div>
);
}
export default TodoItem;
Returns the following:
CONSOLE: Could not consume error: Error {}
PROBLEMS: props.handleChange is not a function
I used binding and I tested to see that my handleChange() was properly defined. Other props are being read but props.handleChange isn't.
Upvotes: 1
Views: 257
Reputation: 3798
you need to bind the map function or use the arrow function. because in this case the this
in 'this.handleChange'
refers to the map's callback function and not to the class that is why it gives you undefined
. so you have two solutions for that problem.
soln#1 : arrow function
render() {
const todoItems = this.state.todos.map( (item) => {
return (
<TodoItem
key={item.id}
text={item.text}
completed={item.completed}
item={item}
handleChange={this.handleChange}
/>
);
});
return <div className="todo-list">{todoItems}</div>;
}
soln#2: bind map's callback function
render() {
const todoItems = this.state.todos.map( function(item) {
return (
<TodoItem
key={item.id}
text={item.text}
completed={item.completed}
item={item}
handleChange={this.handleChange}
/>
);
}.bind(this));
return <div className="todo-list">{todoItems}</div>;
}
Upvotes: 0
Reputation: 88
just change your return function to use an arrow function like:
render() {
const todoItems = this.state.todos.map((item) => {
return (
<TodoItem
key={item.id}
text={item.text}
completed={item.completed}
item={item}
handleChange={this.handleChange.bind(this, item.id)}
/>
);
});
return <div className="todo-list">{todoItems}</div>;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
and change your TodoItem component to something like this:
import React from "react";
function TodoItem(props) {
return (
<div className="todo-item">
<input
type="checkbox"
checked={props.completed}
onChange={props.handleChange} //PROBLEM LINE
/>
<p>{props.text}</p>
</div>
);
}
export default TodoItem;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
demo here
Upvotes: 0
Reputation: 1736
Your problem is here:
const todoItems = this.state.todos.map(function (item) {
return (
<TodoItem
key={item.id}
text={item.text}
completed={item.completed}
item={item}
handleChange={this.handleChange}
/>
);
});
When you say this.handleChange in here you are trying to get handleChange from the function which receives 'item' in the map. handleChange is not defined in that function.
So you need to change the function which receives item to an arrow function which take as this the this of the outer function. Like this:
const todoItems = this.state.todos.map(item =>
<TodoItem
key={item.id}
text={item.text}
completed={item.completed}
item={item}
handleChange={this.handleChange}
/>
);
Upvotes: 2
Reputation: 409
class TestTodo extends React.Component {
state = {
todos: todosData
};
handleChange = (id) => {
console.log(`${id} Clicked!`);
}
render() {
const todoItems = this.state.todos.map( item=> {
return (
<TodoItem
key={item.id}
text={item.text}
completed={item.completed}
item={item}
handleChange={this.handleChange}
/>
);
});
return <div className="todo-list">{todoItems}</div>;
}
}
export default TestTodo;
import React from "react";
const TodoItem = props => {
const handleChange = () =>{
props.handleChange(props.item.id)
}
return (
<div className="todo-item">
<input
type="checkbox"
checked={props.completed}
onChange={handleChange}
/>
<p>{props.text}</p>
</div>
);
}
export default TodoItem;
Upvotes: 2