You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

7.4 KiB

id title permalink prev next
more-about-refs-zh-CN 关于Refs的更多内容 more-about-refs-zh-CN.html working-with-the-browser-zh-CN.html tooling-integration-zh-CN.html

从render方法返回你的UI结构以后,你也许发现自己想“接触”并且调用从 render()返回的组件实例上的方法。通常,这样做对于让数据流过你的应用并不是必须的,因为Reactive的数据流总是确保最新的props 被发送到 render()输出的每个子级。然而,有一些情况下它仍旧是必要或者有益的。

考虑这样一种场景,当你想要告诉一个<input /> 元素(存在于你的实例的子层级)在你更新他的值为空字符串后获得焦点。

  var App = React.createClass({
    getInitialState: function() {
      return {userInput: ''};
    },
    handleChange: function(e) {
      this.setState({userInput: e.target.value});
    },
    clearAndFocusInput: function() {
      this.setState({userInput: ''}); // Clear the input
      // We wish to focus the <input /> now!
    },
    render: function() {
      return (
        <div>
          <div onClick={this.clearAndFocusInput}>
            Click to Focus and Reset
          </div>
          <input
            value={this.state.userInput}
            onChange={this.handleChange}
          />
        </div>
      );
    }
  });

注意,在这个例子中,我们想 "告诉" input 一些东西 - 那些最后它不能从他的props推断出来的东西。在这个场景中我们想 "告诉" 它,它应该转为获得焦点。然而,有一些挑战。从render() 返回的东西不是你实际的 "子" 组件的组合,而是一个在某刻特定实例的子组件的 描述 - 一个快照。

注意:

记住,从 render()返回的东西不是你的 实际的 被渲染出的子组件实例。从 render() 返回的东西仅仅是一个在特定时间你的组件的子层级中的子实例的 描述

这意味着你万万不要把你从render()返回的东西 "抓住不放" 然后期望它变成任何有意义的东西。

  // 反面模式: 这行不通.
  render: function() {
    var myInput = <input />;          // I'm going to try to call methods on this
    this.rememberThisInput = myInput; // input at some point in the future! YAY!
    return (
      <div>
        <div>...</div>
        {myInput}
      </div>
    );
  }

在这个例子中,<input /> 仅仅是一个<input />描述。 这个描述被用于为<input />创建一个 真实的 支持实例(backing instance)

所以我们如何与input的 真实的支持实例对话?

ref String 属性

React支持一种非常特殊的属性,你可以附加到任何从render()输出的组件上。这个特殊的属性允许你引用任何从render()返回的东西的对应 支持实例(backing instance) 。它总是保证是适当的实例,在任何时点上。

就是这么简单:

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

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

    this.refs.myInput
    

你可以直接通过调用React.findDOMNode(this.refs.myInput) 访问组件的DOM node。

ref 回调属性

ref 属性可以是一个回调函数而不是名字。这个回调函数将会在组件挂载以后立即执行。被引用的组件将被作为参数传递,并且回调函数可以立即使用组件,或者保存引用将来使用(或两者都是)。

通过使用ES6的箭头函数,它像添加一个ref属性到任何从render返回的东西一样简单。

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

或者

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

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

完成的示例

  var App = React.createClass({
    getInitialState: function() {
      return {userInput: ''};
    },
    handleChange: function(e) {
      this.setState({userInput: e.target.value});
    },
    clearAndFocusInput: function() {
      // Clear the input
      this.setState({userInput: ''}, function() {
        // This code executes after the component is re-rendered
        React.findDOMNode(this.refs.theInput).focus();   // Boom! Focused!
      });
    },
    render: function() {
      return (
        <div>
          <div onClick={this.clearAndFocusInput}>
            Click to Focus and Reset
          </div>
          <input
            ref="theInput"
            value={this.state.userInput}
            onChange={this.handleChange}
          />
        </div>
      );
    }
  });

在这个例子中, 我们的render函数返回一个 <input /> 实例的描述。但是真正的实例通过 this.refs.theInput 访问。只要带有ref="theInput"的子组件从 render被返回,this.refs.theInput 就可以访问正确的实例。这甚至在更高层(non-DOM)的组件上生效,比如<Typeahead ref="myTypeahead" />

总结

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

优点:

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

注意事项:

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