Sam Sedighian
Sam Sedighian

Reputation: 895

React: Unable to access the parent component's function which is passed as prop to child component

I have a TodoList component which is a child of the App component. I wish to change the state of the App component's todos list. I am attempting to pass a toggleComplete function from the TodoList component to the Todo component so at onClick event it would fire and work its way up to the App component so I could update the state.

I get a "Uncaught TypeError: Cannot read property 'toggleComplete' of undefined" in the TodoList.js

~/src/component/Todo.js

import React, {PropTypes} from 'react';

export default class Todo extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <li className={this.props.todo.done ? 'completed' : 'view'}>
        <div className="view">
          <input onClick={this.props.toggleComplete(this.props.id)} className="toggle" type="checkbox" checked={this.props.todo.done} />
          <label>{this.props.id}: {this.props.todo.title}</label>
          <button className="destroy"></button>
        </div>
      </li>
    );
  }
}

~/src/component/TodoList.js

import React, {PropTypes} from 'react';
import Todo from './Todo'

export default class TodoList extends React.Component {
  constructor(props) {
    super(props);
  }
  toggleComplete(todoID){
    console.log('hit toggleComplete TodoList');
  }

  render() {
    return (
      <section className="main">
        <ul className="todo-list">
          {this.props.todos.map(function(todo, index){
            return <Todo todo={todo} toggleComplete={this.toggleComplete} id={index + 1} key={index+1}/>;
          })}
        </ul>
      </section>
    );
  }
}

~/src/App.js

import React, { Component } from 'react';
import Header from './component/Header'
import TodoList from './component/TodoList'
import TodoFooter from './component/TodoFooter'
import Footer from './component/Footer'

export default class App extends Component {
  constructor(){
    super();
    this.state = {
      todos: [
        {title: 'Taste JavaScript', done: true},
        {title: 'Buy Unicorn', done: false},
        {title: 'eat all day', done: false},
        {title: 'sleep all night', done: true}
      ]
    }
  }

  render() {
    return (
      <div>
        <section className="todoapp">
          <Header />
          <TodoList todos={this.state.todos} />
          <TodoFooter />
        </section>
        <Footer />
      </div>
    );
  }
}

Upvotes: 2

Views: 1817

Answers (2)

Hemadri Dasari
Hemadri Dasari

Reputation: 33994

The above answer can also be done using arrow function without binding

{this.props.todos.map((todo, index) => {
  return <Todo todo={todo} toggleComplete={this.toggleComplete} id={index + 1} key={index+1}/>;
});

Upvotes: 0

Hypaethral
Hypaethral

Reputation: 1477

Your problem seems to occur before the function makes it to your child component, as the error is coming from your parent component. Your map function does not have access to the correct this, so it is treated as undefined -- try this instead:

{this.props.todos.map(function(todo, index){
  return <Todo todo={todo} toggleComplete={this.toggleComplete} id={index + 1} key={index+1}/>;
}, this)} // use the optional "this" argument to the map function

And here's a fiddle to play with showing a simple example of parents rendering their many children with the same reference to the parent's scope: https://jsfiddle.net/v5wd6Lrg/1/

Upvotes: 3

Related Questions