在事件处理程序中获取对React组件的引用。

46

我可以将事件处理程序附加到React组件。有没有一种方法可以在事件处理程序内部获取对该组件的引用?

var Foobar = React.createClass({
    action: function () {
        // ...
    },
    render: function () {
        var child = React.Children.only(this.props.children),
            props = _.omit(this.props, 'children');
        return React.addons.cloneWithProps(child, props);
    }
});

var App = React.createClass({
    handleMouseEnter: function (event) {
        // How to get reference to Foobar without using this.refs['foo']?
        // I need to call *action* on it.
    },
    render: function () {
        return (
            <div>
                <Foobar ref="foo" onMouseEnter={this.handleMouseEnter}>
                    ...
                </Foobar>
            </div>
        );
    }
});

1
为什么不在Foobar组件内定义handleMouseEnter并通过this访问它呢? - daniula
@daniula 因为 Foobar.action 方法可能会作为 onClick 事件的结果被调用,而不仅仅是 onMouseEnter - vbarbarosh
1
action 应该在 Foobar 实例上定义。this.refs.foo.action() 对你无效吗? - Ross Allen
1
你能提供一些上下文信息来说明onClick是由什么触发的吗?这是一个带有onClick处理程序的React组件,还是指的是React映射到App组件中的处理程序之外的某些东西? - Dan W
1
我不明白。如果 this.refs.foo 能够工作,为什么你不想这样做呢? - Ross Allen
显示剩余3条评论
5个回答

43
我认为我理解vbarbarosh提出的问题,或者至少我曾经有一个类似的问题,导致我来到了这篇文章。所以如果这不能回答原始问题,希望它能帮助其他来到这里的人。
在我的情况下,我有一个React组件,其中包含n个子元素,用于定义UI操作的配置选项。每个子元素都有一个不同的引用,标识输入代表什么配置选项,并且我想直接访问引用,以便我可以调用我的子组件上公开的方法。(我可以公开数据属性并使用jQuery进行提取,但那似乎是很多额外的步骤和性能问题)
我的第一次尝试是这样的:
...
<Config ref="showDetails" onChange={this.handleChange} />
<Config ref="showAvatar" onChange={this.handleChange} />
...

理想情况下,我希望将所有更改事件绑定到一个单一处理程序,然后从分派事件的目标中提取引用。不幸的是,分派的SyntheticEvent没有提供获取目标引用的方法,因此我无法直接调用this.ref[name].methodIWantToCall()。
我发现的是React文档中的一篇文章,它让我找到了解决方案:

https://facebook.github.io/react/tips/communicate-between-components.html

我们可以利用JavaScript绑定并传入额外的参数。
...
<Config ref="showDetails" onChange={this.handleChange.bind(this, 'showDetails')} />
...

现在在我的处理程序中,我获得了附加数据并可以访问我的引用:
handleChange: function(refName, event) {
  this.refs[refName].myMethodIWantToCall()
}

技巧在于绑定时,参数顺序会改变,第一个参数现在是传入的绑定值,而事件现在是第二个参数。希望能帮到你!


6
这是一个优雅的解决方案,然而因为性能原因而不被推荐使用。参见https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md。 - Alex Ciminian
1
在React中,像许多其他事情一样,仅仅因为它被不鼓励使用,并不意味着它总是一个坏主意。对于在渲染方法中使用闭包的担忧只有在以下情况下才相关:您创建了引用,这些引用导致处理程序无法被垃圾回收,和/或者您将实例化大量该组件(和/或者如果组件重新渲染了大量次数)。从根本上说,绑定方法的性能成本非常小;只有当它被复合时才会成为问题。 - machineghost
字符串引用现已弃用:https://facebook.github.io/react/docs/refs-and-the-dom.html - Игорь Щербаков
你知道如果ref不是字符串,该如何执行<Config ref="showDetails" onChange={this.handleChange.bind(this, 'showDetails')} />吗?如果要像这样执行<Config ref={(config) => {this.config = config;}} onChange={this.handleChange.bind(this, this.config)} />,你会怎么做? - dhaliman
推荐或不推荐,我真的很喜欢这个解决方案,非常易读。 - leandro lavore

6
您需要将处理程序传播到子组件的根元素,类似于以下方式:

您需要将处理程序传播到子组件的根元素,类似于以下方式:

var Foobar = React.createClass({
    action: function (e) {
        this.props.onClick(this);
    },
    render: function () {
        return <div onClick={this.action}>{this.props.children}</div>;
    }
});

var App = React.createClass({
    handleMouseEnter: function (foobar) {
       console.log(foobar);
    },
    render: function () {
        return (
            <Foobar ref="foo" onClick={this.handleMouseEnter} />
        );
    }
});

肯定是实现这个的最简单方法。谢谢! - TFischer

4
在你提供的示例中,handleMouseEnter(而onClick是传递给Foobar的处理程序)默认情况下由React自动绑定到App实例的上下文中。

因此,在您描述的上下文中,应该使用this.refs.foothis.refs ['foo'] ,这将是正确的方法。

如果没有强烈的理由要将处理程序保留在App中,则更清晰的解决方案是完全将处理程序包含在Foobar中,类似于以下内容:
var Foobar = React.createClass({
    action: function () {
        // ...
    },
    render: function () {
        var child = React.Children.only(this.props.children),
            props = _.omit(this.props, 'children');
        return (
            <div onClick={this.action} onMouseEnter={this.action}>
                React.addons.cloneWithProps(child, props);
            </div>
        );
    }
});

var App = React.createClass({
    render: function () {
        return (
            <Foobar />
        );
    }
});

й—®йўҳжҳҜвҖңеҰӮдҪ•еңЁдәӢ件еӨ„зҗҶзЁӢеәҸдёӯиҺ·еҸ–еҜ№Foobarзҡ„еј•з”ЁпјҢиҖҢдёҚдҪҝз”Ёthis.refs['foo']пјҹвҖқиҝҷдёҚжҳҜжҲ‘жүҖйңҖиҰҒзҡ„гҖӮ - vbarbarosh
你能提供更多的上下文吗?在React中,this.refs是唯一正确且唯一可能的选项。什么情况下需要使用替代机制来引用子组件,而不是使用专为此设计的正确作用域内置功能? - Dan W

0

如果您是以编程方式添加处理程序的,您可以通过在 addEventListener 中使用另一个函数包装处理程序回调来将组件作为参数传递。

我有一个使用滚动事件的示例:

componentDidMount() {
    document.getElementById('theElementId').addEventListener('scroll', (e) => {
        this.handleScroll(e, this);
    });
}

handleScroll(event, _self) {
    // Now you can access to the element using the param _self
}

render() {
    return (
        <div id="theElementId"></div>
    )
}

另一个选择是使用绑定(bind)方法,它会改变this的值:
componentDidMount() {
    document.getElementById('theElementId').addEventListener('scroll', this.handleScroll.bind(this));
}

handleScroll(event) {
    // Now you can access to the element using the param this
}

0

嗯,看看你的App组件,你可以尝试这样做:

var App = React.createClass({
handleMouseEnter: function (event) {
    // reference it by using: this.refs[event._targetInst._currentElement.ref]
    // same result as calling: this.refs.foo
    console.log(this.refs[event._targetInst._currentElement.ref])
},
render: function () {
    return (
        <div>
            <Foobar ref="foo" onMouseEnter={this.handleMouseEnter}>
                ...
            </Foobar>
        </div>
        );
    }
});

希望这可以帮到你。 :) 另外,我正在使用ES6的方式编写/声明React组件,这也会稍微改变“this”的行为。不确定这是否在这里也起作用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接