Edit on GitHub

对组件的refs

在建立你的组件以后,你也许发现你想“接触”并且调用从 render()返回的组件实例上的方法。大部分情况下,这是不必要的因为响应式的数据流总是确保最近的 props 被送到每一个从 render() 输出的子级。然而,有一些情况下它仍旧是必要或者有益的,所以 React 提供了一个被称为 refs 的安全舱口。这些 refs (引用) 在你需要时特别有用 如:查找被组件绘制的 DOM 标记(例如,绝对定位它),使用 React 组件在一个大的非React组件里,或者转换你已有的代码库到React。

让我们来看看怎样取得一个ref,然后深入完整的例子。

从 ReactDOM.render 返回的 ref #

不要被你在你的组件(它返回一个虚拟的DOM元素)里定义的 render() 迷惑了, ReactDOM.render() 会返回一个对你的组件的 backing instance 的引用(无状态组件 返回 null).

var myComponent = ReactDOM.render(<MyComponent />, myContainer);

记住,不管怎样,JSX不会返回一个组件的实例!它只是一个 ReactElement: 一个轻量级的表达,告诉React被挂载的组件应该长什么样。

var myComponentElement = <MyComponent />; // 这只是一个 ReactElement.

// 省略一些代码 ...

var myComponentInstance = ReactDOM.render(myComponentElement, myContainer);
myComponentInstance.doSomething();

注意:

这只应该用在顶层上。在组件内部,让你的 propsstate 来处理和子组件的通信,或者使用其他获取ref的方法(string attribute or callbacks)。

ref Callback 属性 #

React支持一种非常特殊的属性,你可以附加到任何的组件上。 ref 属性可以是一个回调函数,这个回调函数会在组件被挂载后立即执行。被引用的组件会被作为参数传递,回调函数可以用立即使用这个组件,或者保存引用以后使用(或者二者皆是)。

简单的说就是添加一个 ref 属性到任何从 render 返回的东西上:

  render: function() {
    return (
      <TextInput
        ref={function(input) {
          if (input != null) {
            input.focus();
          }
        }} />
    );
  },

或者使用ES6的箭头函数:

  render: function() {
    return <TextInput ref={(c) => this._input = c} />;
  },
  componentDidMount: function() {
    this._input.focus();
  },

当连接一个ref到一个DOM组件如 <div />,你取回DOM节点;当连接一个ref到一个复合组件如 <TextInput />,你会得到React类的实例。在后一种情况下,你可以调用任何那个组件的类暴露的方法。

注意当被引用的组件卸载和每当ref变动,旧的ref将会被以null做参数调用。这阻止了在实例被保存的情况下的内存泄露,如第二个例子。注意当像在这里的例子,使用内联函数表达式写refs,React在每次更新都看到不同的函数对象,ref将会被以null 立即调用在它被以组件实例调用前。

ref String 属性 #

React同样支持使用一个字符串(代替回调函数)在任意组件上作为一个 ref prop,尽管这个方法在这点上主要是遗留物。

  1. 赋值ref属性为任何从render 返回的东西,比如:

    <input ref="myInput" />
    
  2. 在其他一些代码中(典型的如事件处理的代码),通过this.refs访问 支持实例(backing instance),如:

    var input = this.refs.myInput;
    var inputValue = input.value;
    var inputRect = input.getBoundingClientRect();
    

完整的示例 #

为了获取一个React组件的引用,你既可以使用 this 来获取当前的React组件,也可以使用一个 ref 来获取一个你拥有的组件的引用。他们像这样工作:

var MyComponent = React.createClass({
  handleClick: function() {
    // Explicitly focus the text input using the raw DOM API.
    this.myTextInput.focus();
  },
  render: function() {
    // The ref attribute adds a reference to the component to
    // this.refs when the component is mounted.
    return (
      <div>
        <input type="text" ref={(ref) => this.myTextInput = ref} />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.handleClick}
        />
      </div>
    );
  }
});

ReactDOM.render(
  <MyComponent />,
  document.getElementById('example')
);

在这个例子中,我们获得一个对 text input backing instance 的引用 并且当按钮被点击时我们调用 focus()

对于复合组件,引用会指向一个组件类的实例所以你可以调用那个类定义的任何方法。如果你需要访问那个组件的底层的DOM节点,你可以使用 ReactDOM.findDOMNode 作为一个 安全舱口 但是我们不推荐这样,因为它打破了封装,在大多数情况下都有一个清晰的方法来以React模式构建你的代码.

总结 #

Refs是一种很好的发送消息给特定子实例(通过流式的Reactive propsstate来做会不方便)的方式。它们应该,不论怎样,不是你的应用中数据流通的首选。默认情况下,使用响应式数据流,并为本身不是reactive的用例保存ref

优点: #

  • 你可以在你的组件类里定义任何的公开方法(比如在一个Typeahead的重置方法)然后通过refs调用那些公开方法(比如this.refs.myTypeahead.reset())。在大多数情况下,使用内建的React数据流更清晰,而不是使用强制的ref。
  • 实行DOM测量几乎总是需要接触到 "原生" 组件比如 <input /> 并且通过 ref 访问它的底层DOM节点。 Refs 是唯一一个可靠的完成这件事的实际方式。
  • Refs 是为你自动管理的!如果子级被销毁了,它的ref也同样为你销毁了。这里不用担心内存(除非你做了一些疯狂的事情来自己保持一份引用)。

注意事项: #

  • 绝不 在任何组件的 render 方法中访问 refs - 或者当任何组件的render方法还在调用栈上的任何地方运行时。
  • 如果你想要保留Google Closure Compiler advanced-mode crushing resilience,务必不要以属性的方式访问指明为字符串的属性。这意味这你必须用this.refs['myRefString']访问,如果你的ref被定义为ref="myRefString"
  • 如果你没有用React写过数个程序,你的第一反应通常是打算试着用refs来在你的应用里"让事情发生"。如果是这样,花一些时间并且更精密的思考state应该属于组件层级的哪个位置。常常,这会变得清晰:正确的"拥有"那个属性的地方应该在层级的更高层上。把state放在那里 往往消除了任何使用refs 来 "让事情发生"的渴望 - 作为替代,数据流通常将完成你的目标。
  • Refs 不能连接到一个 stateless function(无状态函数),因为这些组件没有支持实例。你总是可以包装一个无状态组件在一个标准复合组件里并且连接一个ref到这个复合组件。