【问题标题】:Get reference to React component in event handler在事件处理程序中获取对 React 组件的引用
【发布时间】:2015-01-20 15:51:17
【问题描述】:

我可以将我的事件处理程序附加到一个 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>
        );
    }
});

【问题讨论】:

  • 为什么不在Foobar组件内部定义handleMouseEnter并通过this访问呢?
  • @daniula 因为Foobar.action 方法可能由于onClick 事件而被调用,而不仅仅是onMouseEnter
  • action 应该在 Foobar 实例上定义。 this.refs.foo.action() 不适合你吗?
  • 你能给出一些触发 onClick 的上下文吗?这会是一个带有 onClick 处理程序的反应组件,还是您指的是 React 映射到 App 组件中的处理程序之外的东西?
  • 我不明白。如果this.refs.foo 有效,你为什么不想这样做?

标签: reactjs


【解决方案1】:

我想我理解 vbarbarosh 提出的问题,或者至少我有一个类似的问题导致我发了这篇文章。因此,如果这不能回答最初的问题,希望它可以帮助到这里的其他人。

在我的例子中,我有一个带有 n 个子元素的 React 组件,用于定义 UI 操作的配置选项。每个孩子都有一个不同的 ref 标识输入代表什么配置选项,我希望能够直接访问 ref,这样我就可以调用在我的子组件上公开的方法。 (我可以公开数据属性并使用 jQuery 来提取,但这似乎有很多额外的箍和性能问题)

我的第一次尝试是这样的:

...
<Config ref="showDetails" onChange={this.handleChange} />
<Config ref="showAvatar" onChange={this.handleChange} />
...

理想情况下,我想将所有更改事件绑定到单个处理程序,然后从调度事件的目标中提取 ref。不幸的是,已调度的 SyntheticEvent 确实 提供了一种获取目标 ref 的方法,因此我无法直接调用 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()
}

诀窍在于,在绑定时,参数顺序发生了变化,第一个参数现在是传入的绑定值,而事件现在是第二个参数。希望对您有所帮助!

【讨论】:

  • 这是一个优雅的解决方案,但由于性能原因不鼓励使用。见github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/…
  • 就像 React 中的许多事情一样,仅仅因为不鼓励它并不意味着它总是一个坏主意。对渲染方法中的闭包的关注仅在以下情况下相关:您创建导致处理程序不被垃圾收集的引用和/或您将实例化大量该组件(和/或如果组件重新渲染疯狂次数)。从根本上说,绑定方法的性能成本很小;只有复合时才有问题。
  • 如果 ref 不是字符串,你知道如何处理 &lt;Config ref="showDetails" onChange={this.handleChange.bind(this, 'showDetails')} /&gt; 吗?你会怎么做这样的事情&lt;Config ref={(config) =&gt; {this.config = config;}} onChange={this.handleChange.bind(this, this.config)} /&gt;
  • 推荐不推荐,我很喜欢这个方案,可读性很强
【解决方案2】:

您必须将处理程序传播到子组件的根元素,如下所示:

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} />
        );
    }
});

【讨论】:

  • 绝对是实现这一目标的最简单方法。谢谢!
【解决方案3】:

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 />
        );
    }
});

【讨论】:

  • 问题是“如何在不使用this.refs['foo'] 的情况下获取Foobar 的引用(内部事件处理程序)?”。这不是我需要的。
  • 你能提供更多的背景信息吗? this.refs 是 React 中唯一正确且唯一可能的选项。什么是确切的场景,这意味着您需要一种替代机制来使用为引用子组件而设计的正确范围的内置功能?
【解决方案4】:

如果您以编程方式添加处理程序,您可以将组件作为参数传递,只需在 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>
    )
}

另一种选择是使用将更改this 的值的绑定方法:

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

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

【讨论】:

    【解决方案5】:

    看看你的应用组件,你可以试试这个:

    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”的行为。不确定这是否也可能在这里发挥作用。

    【讨论】:

      猜你喜欢
      • 2013-05-06
      • 1970-01-01
      • 2020-09-18
      • 2012-05-26
      • 2018-01-25
      • 1970-01-01
      • 2010-09-09
      • 1970-01-01
      • 2017-01-08
      相关资源
      最近更新 更多