【问题标题】:scope of "this" in module.exportsmodule.exports 中“this”的范围
【发布时间】:2015-09-11 12:17:35
【问题描述】:

我正在使用 module.exports 将 React.js 组件转换为 Common.js 模块,并且在从其中一种方法访问组件元素上下文中的“this”时遇到问题。

下面是整个组件。我在出现问题的行上方添加了注释。一开始我确实尝试了一个不太冗长的例子,但我认为这不足以解释这个问题。

    var React = require('react');
    var GSAP  = require('gsap');

    var Psychedelicon = React.createClass({  

        cycleColors: function() {
            var touchPlatforms = ['iPhone', 'iPad', 'iPod', 'Android', 'Linux armv7l', 'WinCE'];
                       isTouch  = false;
                       iDevice  = false;
                       isDroid  = false;
                       plat = navigator.platform;

            if(plat === 'iPhone' || plat === 'iPad' || plat === 'iPod') {
                isTouch  = true;
                iDevice  = true;
            }
            else if (plat === 'Linux armv7l' || plat === 'Android') {
                isTouch  = true;
                isDroid  = true;
            }
            else {
                for (var i = 0; i < touchPlatforms.length; i++) {
                    if (plat === touchPlatforms[i]) {
                        isTouch = true;
                        break;
                    }
                    else {
                        isTouch = false;
                    }
                }
            }

            var isIE = false
                if (navigator.userAgent.toLowerCase().indexOf('msie') > -1 || navigator.userAgent.toLowerCase().indexOf('trident') > -1) {
                isIE = true
            }

            var isFF = false
                if (navigator.userAgent.toLowerCase().indexOf('firefox') != -1) {
                isFF = true
            }

            if(!isTouch) {
                var ColorSwirl = function(colorSet,defaultColor,time) {
                    var storedResult;
                    var randomColor = function(theArray) {
                        var result = theArray[Math.floor(Math.random() * (theArray.length))];
                        if(result === storedResult){
                            return(defaultColor)                        
                        }
                        else {
                            storedResult = result;
                            return(result);
                        }
                    }
                    var theLuckyColors = {top:randomColor(colorSet),bottom:randomColor(colorSet)};
                    var swirl = function(){
    //!!!!On this line the problem occurs onUpdateParams must reference the element accepting the execution event (onMouseEneter)
                        TweenLite.to(theLuckyColors, time, {colorProps:{top:randomColor(colorSet), bottom:randomColor(colorSet)}, onUpdate:colorize, onUpdateParams:[this],onComplete:swirl});
                    }
 gradients
                    var colorize = function(el) {
                        if(isIE) {
                            TweenLite.set(el, {
                                backgroundImage:'-ms-radial-gradient(center,circle cover,' + theLuckyColors.top + ' 0%, ' + theLuckyColors.bottom + ' 100%)'
                            });
                        }
                        else if(isFF) {
                            TweenLite.set(el, {
                                backgroundImage:'-moz-radial-gradient(center,circle cover,' + theLuckyColors.top + ' 0%, ' + theLuckyColors.bottom + ' 100%)'
                            });
                        }
                        else {
                            TweenLite.set(el, {
                                backgroundImage:'radial-gradient(circle,' + theLuckyColors.top + ', ' + theLuckyColors.bottom + ')',
                                backgroundImage:'-webkit-radial-gradient(circle,' + theLuckyColors.top + ', ' + theLuckyColors.bottom + ')'
                            });
                        }
                    }
                    swirl();
                }
                ColorSwirl(['red','green','#4B0082','#9F00FF','yellow','orange'],'blue',.15);
            }

        },
        stopTheCycle: function() {

        },

        render: function() {
            return (
                <a className="psychedelicon" href={this.props.href} target={this.props.target} onMouseEnter={this.cycleColors} onMouseLeave={this.stopTheCycle}>
                    <i className={"fa fa-" + this.props.icon}></i>
                </a>
            )
        }

    });
    module.exports = Psychedelicon;

到目前为止,我已经尝试将“this”绑定到接收事件的元素:

onMouseEnter={this.cycleColors.bind(this)}

我得到:`'你正在将一个组件方法绑定到组件。 React 会以高性能的方式自动为您执行此操作,因此您可以安全地删除此调用。'

我也试过了:

onMouseEnter={this.cycleColors.call(Psychedelicon)}

和 onMouseEnter={this.cycleColors.bind(Psychedelicon)}

两者都没有产生错误但没有工作

我知道该功能可以正常工作,因为当我更改时

onUpdateParams:[this]

onUpdateParams:['.psychedelicon']

组件产生所需的行为,除了它同时影响所有组件(我需要避免因此需要使用“this”)。

我一定错过了什么。任何帮助表示赞赏。

【问题讨论】:

  • 你的第二个 sn-p 也不起作用jsfiddle.net/arunpjohny/ssogpue2/1
  • @ArunPJohny 嗯...你是对的,我删除了它
  • 试试$('#foo').click(Module.addClass)!在您的 html sn-p 中,您甚至没有调用该方法。

标签: javascript reactjs commonjs node-modules


【解决方案1】:

更新:此答案不适用于 React,而是针对该问题的更一般的先前版本。


这看起来像是不使用onclick 属性的另一个参数,但您可以使用callapply 方法,并将this 作为第一个参数传递。

<div id="foo" onClick="Module.addClass.call(this)"></div>

但是,您可能要考虑使用addEventListener 或 jQuery 的事件委托。

【讨论】:

  • 对,但我使用的是 React,所以我想内联是要走的路……我会试一试
  • 感谢您的帮助,但这不起作用。为了清楚起见,我更新了问题并添加了整个组件的代码。
  • 好的,我解决了这个问题。由于我描述的问题,React 有一种访问渲染元素的方法,称为 refs。我能够使用它来引用渲染的元素。上面的代码失败了,因为“this”引用了组件的构造函数而不是渲染的元素。
  • @HelloWorld 你应该发布你的解决方案作为答案,我会赞成。
  • 好的,我发布了一些详细的答案,以防有人处理同样的问题。如果允许,我将在 12 小时内接受我自己的答案。
【解决方案2】:

所以我能够解决我自己的问题。这是成功的代码:

var React = require('react');
var GSAP  = require('gsap');
var $     = require('jquery')

var Psychedelicon = React.createClass({

    componentDidMount: function() {

        var that = React.findDOMNode(this.refs.psicon);
        $(that).hover(function() {
    //detect device type for Psychedelicon
            var touchPlatforms = ['iPhone', 'iPad', 'iPod', 'Android', 'Linux armv7l', 'WinCE'];
                       isTouch  = false;
                       iDevice  = false;
                       isDroid  = false;
                       plat = navigator.platform;

            if(plat === 'iPhone' || plat === 'iPad' || plat === 'iPod') {
                isTouch  = true;
                iDevice  = true;
            }
            else if (plat === 'Linux armv7l' || plat === 'Android') {
                isTouch  = true;
                isDroid  = true;
            }
            else {
                for (var i = 0; i < touchPlatforms.length; i++) {
                    if (plat === touchPlatforms[i]) {
                        isTouch = true;
                        break;
                    }
                    else {
                        isTouch = false;
                    }
                }
            }

    //sniff the for ie
            var isIE = false
                if (navigator.userAgent.toLowerCase().indexOf('msie') > -1 || navigator.userAgent.toLowerCase().indexOf('trident') > -1) {
                isIE = true
            }

    //sniff for firefox
            var isFF = false
                if (navigator.userAgent.toLowerCase().indexOf('firefox') != -1) {
                isFF = true
            }


    //Begin ColorSwirl on non-touch devices
            if(!isTouch) {
    //Define the Color Sets
                var ColorSwirl = function(colorSet,defaultColor,time) {
    //Pick random color. If the color is the same as the previous one pick blue instead.
                    var storedResult;
                    var randomColor = function(theArray) {
                        var result = theArray[Math.floor(Math.random() * (theArray.length))];
                        if(result === storedResult){
                            return(defaultColor)                        
                        }
                        else {
                            storedResult = result;
                            return(result)
                        }
                    }
    //Pick our colors for the initial state
                    var theLuckyColors = {top:randomColor(colorSet),bottom:randomColor(colorSet)};
    //Start swirling
                    $(that).addClass('swirling');
                    var swirl = function(){
                        if($(that).hasClass('swirling')) {
                            TweenLite.to(theLuckyColors, time, {colorProps:{top:randomColor(colorSet), bottom:randomColor(colorSet)}, onUpdate:colorize, onUpdateParams:[that],onComplete:swirl});
                        }
                    }
    //Detect Browser and Pass Psychedelicon the appropriate radial gradients
                    var colorize = function(el) {
                        if(isIE) {
                            TweenLite.set(el, {
                                backgroundImage:'-ms-radial-gradient(center,circle cover,' + theLuckyColors.top + ' 0%, ' + theLuckyColors.bottom + ' 100%)'
                            });
                        }
                        else if(isFF) {
                            TweenLite.set(el, {
                                backgroundImage:'-moz-radial-gradient(center,circle cover,' + theLuckyColors.top + ' 0%, ' + theLuckyColors.bottom + ' 100%)'
                            });
                        }
                        else {
                            TweenLite.set(el, {
                                backgroundImage:'radial-gradient(circle,' + theLuckyColors.top + ', ' + theLuckyColors.bottom + ')',
                                backgroundImage:'-webkit-radial-gradient(circle,' + theLuckyColors.top + ', ' + theLuckyColors.bottom + ')'
                            });
                        }
                    }
                    swirl();
                }
                ColorSwirl(['red','green','#4B0082','#9F00FF','yellow','orange'],'blue',.15);
            }

        },function() {
            var theLuckyColors = {top:'#FFFFFF',bottom:'#FFFFFF'};
            var stopNow = function(time){
                $(that).removeClass('swirling');
                TweenLite.to(theLuckyColors, time, {colorProps:{top:'#FFFFFF', bottom:'#FFFFFF'}, onUpdate:whiteWash, onUpdateParams:[that]});
            }
            var whiteWash = function(el) {
                    TweenLite.set(el, {
                        backgroundImage:'-ms-radial-gradient(center,circle cover,#FFFFFF 0%, #FFFFFF 100%)',
                        backgroundImage:'-moz-radial-gradient(center,circle cover,#FFFFFF 0%, #FFFFFF 100%)',
                        backgroundImage:'radial-gradient(circle,#FFFFFF,#FFFFFF)',
                        backgroundImage:'-webkit-radial-gradient(circle,#FFFFFF,#FFFFFF)'
                    });
            }
            stopNow(.15);       
        });
    },
    render: function() {
        return (
            <a className="psychedelicon" ref="psicon" href={this.props.href} target={this.props.target} onMouseEnter={this.cycleColors} onMouseLeave={this.stopTheCycle}>
                <i className={"fa fa-" + this.props.icon}></i>
            </a>
        )
    }

})

module.exports = Psychedelicon;

这是我从问题到解决方案的过程:

当我无法按照@Alexander O'Mara 的建议使用“调用”产生结果时,我需要 jQuery 来加速测试并添加变量

var that = $(this)

到组件的最外层范围,这样我就可以从内部函数的范围内访问组件本身,如下所示:

//Note that onUpdateParams now references "that" which is equal to "this" in the scope of the actual component.
TweenLite.to(theLuckyColors, time, {colorProps:{top:randomColor(colorSet), bottom:randomColor(colorSet)}, onUpdate:colorize, onUpdateParams:[that],onComplete:swirl});

这又失败了,所以我将“this”的值记录到控制台,发现我实际上是在引用组件的构造函数,而不是渲染的输出!

我再次查看了docs,发现我可以通过使用名为“refs”的 reactjs 属性来引用每个渲染实例上的渲染输出。我只需要给渲染的元素一个“ref”属性:

render: function() {
        return (
            <a className="psychedelicon" ref="psicon" href={this.props.href} target={this.props.target} onMouseEnter={this.cycleColors} onMouseLeave={this.stopTheCycle}>
                <i className={"fa fa-" + this.props.icon}></i>
            </a>
        )
    }

并在我的方法中引用 ref,而我决定用完“componentDidMount”。

var that = React.findDOMNode(this.refs.psicon);

现在,每次我引用“那个”时,我都是在引用渲染元素本身(考虑到它在鼠标悬停时每 0.15 秒重新渲染一次,这非常令人印象深刻),一切都很美好!

【讨论】:

    猜你喜欢
    • 2022-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多