2

I am trying to pass a method to a child component to handle onclick events. I saw a lot of examples online, but I can't get it working. When I am inside the render function of the parent and trying to pass "this.handleClick" to the child, handleClick is undefined.

Have a look at render method of ThumbList:

var Thumb = React.createClass({
        handleClick: function() {
            console.log(this)
            console.log('handleClick of Thumb')
            this.props.onClick()
        },
        render: function() {
            return(
                    <div className="thumb" key={this.props.thumb.url}>
                        <a href='#' onClick={this.handleClick}>
                            <img src={'/img/rings/thumbs/'+this.props.thumb.url+'_thumb.jpg'} alt="Image"> 
                            </img>         
                        </a>
                    </div>
                );
        }
    });

    var ThumbList = React.createClass({

        handleClick: function (id) {
            console.log('click of ThumbList');

        },


        loadFromServer: function() {
            $.ajax({
                url: 'rings/imgs/5',
                dataType: 'json',
                success: function(data) {
                    this.setState({data: data});
                }.bind(this),
                error: function(xhr, status, err) {
                    console.error('rings/imgs/5', status, err.toString());
                }.bind(this)
            });
        },
        getInitialState: function(){
            return {data: [] };
        },
        componentDidMount: function(){
              this.loadFromServer();
                setInterval(this.loadFromServer, 2000);
                },

        render: function() {
            var handlefunc=this.handleClick
            var thumbsNodes = this.state.data.map(function(thumb) {
                console.log(this.handleClick)  // is Undefined!
                console.log(handlefunc)   // is working
                return (
                        <Thumb thumb={thumb} key={thumb.url} onClick={handlefunc.bind(this,thumb.url)}/>
                );
            });

            return(
                        <div className="col-md-1 col-md-offset-1" id='thumbs'>
                            {thumbsNodes}
                        </div> 

                );
            }

    });

Any idea what I might be missing?

MLavoie
  • 9,671
  • 41
  • 36
  • 56
avieln
  • 75
  • 3
  • 5
  • 1
    try to add .bind(this) at the end of the function you create in this.state.data.map() – ThePavolC Mar 22 '15 at 09:19
  • Possible duplicate of [Using react props in .map function](http://stackoverflow.com/questions/37155677/using-react-props-in-map-function) – Felix Kling May 11 '16 at 15:23

3 Answers3

5

If you're using a compiler like Babel as part of your development workflow, I'd suggest using arrow functions:

var thumbsNodes = this.state.data.map((thumb) => {
  console.log(this.handleClick);
  return <Thumb thumb={thumb} key={thumb.url} 
                onClick={this.handlefunc.bind(this,thumb.url)}/>;
});

As you can see, it's a nice compact syntax. The arrow function will preserve the this context for you. The Babel compiler produces JavaScript that uses a closure:

var thumbsNodes = this.state.data.map(function(thumb) {
  var _this = this;
  console.log(_this.handleClick);
  return <Thumb thumb={thumb} key={thumb.url} 
                onClick={_this.handlefunc.bind(_this,thumb.url)}/>;
});
WiredPrairie
  • 58,954
  • 17
  • 116
  • 143
3

this is undefined because the map callback does not know what it is. The simplest way to solve this is to pass a second argument, and it will use that as this in the callback:

var thumbsNodes = this.state.data.map(function(thumb) {
  console.log(this.handleClick)
  return <Thumb thumb={thumb} key={thumb.url} onClick={handlefunc.bind(this,thumb.url)}/>
}, this)

More: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

David Hellsing
  • 106,495
  • 44
  • 176
  • 212
  • thanks! i didnt realize that the map function have no "this" context of the calling object. this solved this – avieln Mar 24 '15 at 06:23
0

You need to grab a reference to this so it is the correct context when you call it.

Try

render: function() 
{
  var handlefunc = this.handleClick; // i assume this is just for debugging the issue
  var self = this;
  var thumbsNodes = this.state.data.map(function(thumb) 
  {
     console.log(self.handleClick)  // note the use of `self`
  });
}
click2install
  • 980
  • 9
  • 23
  • The other answer is cleaner. Using the trick self=this, that=this is working but there is more elegant solution – Jeremy D Mar 24 '15 at 16:04
  • this solves your issue, but it’s not very functional to use globally scoped variables like this, f.ex if you would want to re-use the callback elsewhere. – David Hellsing Mar 24 '15 at 20:24
  • @David I agree, but a call to the function would resolve this issue - e.g., `handleClick.call(self);` I also agree that the scope argument to the `map` is the best approach but given the question I assumed that for the poster my suggested approach would be 'more readible'. When scoping is not well understood, having `this` everywhere that means different things can be more confusing than a deliberate re-scoping as in my example. – click2install Mar 25 '15 at 01:25