Reputation: 429
Where did I get wrong?. I've been following a tutorial but it always gives me this error. TypeError: Cannot read property 'map' of undefined
. and What would cause the prop to become undefined.
here's my code:
var todoItems = [];
todoItems.push({index: 1, value: "learn react", done: false});
todoItems.push({index: 2, value: "Go shopping", done: true});
todoItems.push({index: 3, value: "buy flowers", done: true});
class TodoList extends React.Component {
render () {
var items = this.props.items.map((item, index) => {
return (
<TodoListItem key={index} item={item} index={index} removeItem={this.props.removeItem} markTodoDone={this.props.markTodoDone} />
);
});
return (
<ul className="list-group"> {items} </ul>
);
}
}
class TodoListItem extends React.Component {
constructor(props) {
super(props);
this.onClickClose = this.onClickClose.bind(this);
this.onClickDone = this.onClickDone.bind(this);
}
onClickClose() {
var index = parseInt(this.props.index);
this.props.removeItem(index);
}
onClickDone() {
var index = parseInt(this.props.index);
this.props.markTodoDone(index);
}
render () {
var todoClass = this.props.item.done ?
"done" : "undone";
return(
<li className="list-group-item ">
<div className={todoClass}>
<span className="glyphicon glyphicon-ok icon" aria-hidden="true" onClick={this.onClickDone}></span>
{this.props.item.value}
<button type="button" className="close" onClick={this.onClickClose}>×</button>
</div>
</li>
);
}
}
class TodoForm extends React.Component {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
}
componentDidMount() {
this.refs.itemName.focus();
}
onSubmit(event) {
event.preventDefault();
var newItemValue = this.refs.itemName.value;
if(newItemValue) {
this.props.addItem({newItemValue});
this.refs.form.reset();
}
}
render () {
return (
<form ref="form" onSubmit={this.onSubmit} className="form-inline">
<input type="text" ref="itemName" className="form-control" placeholder="add a new todo..."/>
<button type="submit" className="btn btn-default">Add</button>
</form>
);
}
}
class TodoHeader extends React.Component {
render () {
return <h1>Todo list</h1>;
}
}
export default class TodoApp extends React.Component {
constructor (props) {
super(props);
this.addItem = this.addItem.bind(this);
this.removeItem = this.removeItem.bind(this);
this.markTodoDone = this.markTodoDone.bind(this);
this.state = {todoItems: todoItems};
}
addItem(todoItem) {
todoItems.unshift({
index: todoItems.length+1,
value: todoItem.newItemValue,
done: false
});
this.setState({todoItems: todoItems});
}
removeItem (itemIndex) {
todoItems.splice(itemIndex, 1);
this.setState({todoItems: todoItems});
}
markTodoDone(itemIndex) {
var todo = todoItems[itemIndex];
todoItems.splice(itemIndex, 1);
todo.done = !todo.done;
todo.done ? todoItems.push(todo) : todoItems.unshift(todo);
this.setState({todoItems: todoItems});
}
render() {
return (
<div id="main">
<TodoHeader />
<TodoList items={this.props.initItems} removeItem={this.removeItem} markTodoDone={this.markTodoDone}/>
<TodoForm addItem={this.addItem} />
</div>
);
}
}
and it always points to the var items = this.props.items.map((item, index) => ...
. I don't think this tutorial has an error. I'm still new in React.
Upvotes: 0
Views: 1031
Reputation: 104499
There is no issue with tutorial, but you missed one part, Check the last line:
ReactDOM.render(<TodoApp initItems={todoItems}/>, ....);
There todoItems
is passed into component in props so this.props.todoItems
will have the initial data, but in your case you are exporting the component and rendering it somewhere else and not passing the data to TodoApp
component that's why this.props.todoItems
is undefined
.
Since you define the data in the same file, So use the local data directly.
Write it like this:
<TodoList items={todoItems} ....
Other solution:
Instead of defining the data into this file define it where you are importing TodoApp and rendering it, and pass the data from that place it will work.
Like this:
import TodoApp from '/path_to_todoapp/';
var todoItems = [];
todoItems.push({index: 1, value: "learn react", done: false});
todoItems.push({index: 2, value: "Go shopping", done: true});
todoItems.push({index: 3, value: "buy flowers", done: true});
ReactDOM.render(<TodoApp initItems={todoItems}/>, document.getElementById('app'));
Update:
Use ReactDOM.render
, check this answer for more details: React vs ReactDOM?
Upvotes: 1