3

For example I have a react component with two binding methods:

import React from 'react';

class Comments extends React.Component {
    constructor(props) {
        super(props);
        this.handleSubmit = this.handleSubmit.bind(this);
        this.handleRemoveComment = this.handleRemoveComment.bind(this);
    }

    handleSubmit(e) {
        .....
    }

    handleRemoveComment(e) {
        //this.props.removeComment(null, this.props.params, i);
    }

    renderComment(comment, i) {
        return(
            <div className="comment" key={i}>
                  .....
                  <button 
                       onClick={this.handleRemoveComment}
                       className="remove-comment">
                       &times;
                  </button>
            </div>
        )
    }

    render() {
        return(
            <div className="comments">

                {this.props.postComments.map(this.renderComment)}

                .....
            </div>
        )
    }
}

export default Comments;

In above code, I have two binding method: one is handleSubmit and one is handleRemoveComment. handleSubmit function worked but handleRemoveComment doesn't. When running, It returns error:

TypeError: Cannot read property 'handleRemoveComment' of undefined

Mayank Shukla
  • 100,735
  • 18
  • 158
  • 142
Trần Kim Dự
  • 5,872
  • 12
  • 55
  • 107

1 Answers1

7

Issue is with this line:

{this.props.postComments.map( this.renderComment )}

Because you forgot to bind renderComment, map callback method, so this inside renderComment method will not refer to the class context.

Use any one of these solutions, it will work.

1- Use this line in constructor:

this.renderComment = this.renderComment.bind(this) ;

2- Pass this with with map like:

{this.props.postComments.map(this.renderComment, this)}

3- Use Arrow function with renderComment method, like this:

renderComment = (comment, i) => {
    .....

or use the map inside the renderComment function (i used to prefer this way), like this:

renderComment() {
    return this.props.postComments.map((comment, i) => {
        return(
            <div className="comment" key={i}>
                <p>
                    <strong>{comment.user}</strong>
                    {comment.text}
                    <button 
                        onClick={this.handleRemoveComment}
                        className="remove-comment">
                        &times;
                    </button>
                </p>
            </div>
        )
    })
}

And call this method from render, in this case binding of renderComment is not required:

{this.renderComment()}
Mayank Shukla
  • 100,735
  • 18
  • 158
  • 142
  • 3
    ...or just add `, this` in the call to `map`: `{this.props.postComments.map(this.renderComment, this)}` – T.J. Crowder Apr 23 '17 at 07:08
  • Wow. thanks so much. I think there is some problem in `handleRemoveComment` because error message. I have two questions: 1. why can you know error in `renderComment` (by reading source code carefully or by your experience ...) 2. If I remove code `onClick={this.handleRemoveComment}`, no error found. – Trần Kim Dự Apr 23 '17 at 07:13
  • reason is: if you don't bind the context then `this` keyword (refer to class) will not be available inside `map` body, that why when you removed the `removeComment` it is working, if you want to use `this` keyword you need to bind the context. By reading the source code its a very common issue with map body :) – Mayank Shukla Apr 23 '17 at 07:17
  • check the updated answer, you can try any one solution, Thanks @T.J.Crowder for very simple solution :) – Mayank Shukla Apr 23 '17 at 07:23