Ray Suelzer
Ray Suelzer

Reputation: 4107

Scoping 'this' within a reactjs component

While building my first reactjs component, I got the familiar "undefined is not a function" when trying to use a helper (makeWorkSiteUrl) because the this scope had changed away from being that of the component.

Of course, I just did what most JS developers would do and declared that=this before I made my function call to solve the problem.

However, it occurred to me that this is probably a common problem. So there might be a 'better' or 'react' way of achieving the same thing. Or, it may be an indication of poor design of this component. Is there a style or method that is preferred or available instead of that=this to access a components internal functions/properties?

"Undefined is not a function":

var WorkSiteQuickList = React.createClass({
    propTypes: {
        data: React.PropTypes.object.isRequired     
    },
    render() {
        var workSiteNodes = this.props.data.work_sites.map(function (worksite) {
            var linkUrl = this.makeWorkSiteUrl(worksite.id) //Fail  
            return (
                <WorkSiteQuickListItem workSiteName={worksite.name} linkUrl={linkUrl} />
            );
        });
        return (
                <div>    
                    {workSiteNodes}             
               </div>
        );
    },
    makeWorkSiteUrl(workSiteId) {
        return "/worksite/"+ workSiteId;
    }   
});

Works as expected:

var WorkSiteQuickList = React.createClass({
    propTypes: {
        data: React.PropTypes.object.isRequired     
    },
    render() {
        that = this;
        var workSiteNodes = this.props.data.work_sites.map(function (worksite) {
            var linkUrl = that.makeWorkSiteUrl(worksite.id) //OK  
            return (
                <WorkSiteQuickListItem workSiteName={worksite.name} linkUrl={linkUrl} />
            );
        });
        return (
                <div>    
                    {workSiteNodes}             
               </div>
        );
    },
    makeWorkSiteUrl(workSiteId) {
        return "/worksite/"+ workSiteId;
    }   
});

I'm sure this question will be closed, but I'd rather ask it than miss a critical part of the "framework".

Upvotes: 2

Views: 314

Answers (1)

Gil Birman
Gil Birman

Reputation: 35900

Here are some common patterns to deal with this scoping:

  • Utilizing an es6 transpiler (like the JSX transformer that you're currently using, or babeljs which happens to support JSX), use arrow functions which are scoped to the outer context (aka lexical scoping).

     this.props.data.work_sites.map( (worksite) => { .... return ... } )
    
  • Pass in this as the second argument of Array#map

  • Chain bind onto the function you pass into Array#map

    function() {}.bind(this)
    

Update: I've had a lot of trouble lately with =>, this resolving to window when paused at a breakpoint in the chrome dev tools within the render() method. Originally I thought it was a babeljs bug. However, turns out it's actually a failure of the dev tools ability to understand that source maps actually replace this with _this.

Upvotes: 3

Related Questions