7.4 KiB
id | title | permalink | prev | next |
---|---|---|---|---|
more-about-refs | 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: ''}); //清空输入
// 现在我们希望获取焦点<input />!
},
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 />; // 我打算在这个上调用这个方法
this.rememberThisInput = myInput; // input在未来的某个时间点上!YAY!
return (
<div>
<div>...</div>
{myInput}
</div>
);
}
在这个例子中,<input />
仅仅是 <input />
的一个描述。这个描述不能用来为<input />
创建一个真实的 背后的实例。
那么对 input 的真实的 背后的实例,该怎么说呢?
ref 字符串属性
React 假设你可以向任何的从render()
中返回的组件附加一个特别的属性。这个特殊的属性允许你引用任何的从render()
返回的东西对于有的背后的实例。而且保证在任何时间点都是合适的实例。
简单的说就是:
-
给任何从
render()
中返回的东西赋一个ref
的属性,如下所示:<input ref="myInput" />
-
在其他的代码中(通常是事件处理代码中),通过
this.refs
来访问 背后的实例 如下所示:this.refs.myInput
通过调用this.refs.myInput
你可以直接方法这个组建的DOM。
ref 回调属性
ref
属性可以用一个回调函数来替换一个名字。当这个组件装载好之后立即执行。被引用的组件被作为一个参数传入,这个回调函数可能立即使用这个组件,或保存这个引用,将来再用(或者两者都做)。
使用ES6的箭头函数,为从render
中返回的东西添加一个ref
属性非常简单:
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();
}
}} />
);
},
注意当引用的组件被卸载以及无论何时这个引用改变之后,旧的引用将会被用null
作为一个参数来调用。这避免了当实例被存储的情形下- 像第一个例子那样 - 引起的内存泄漏。注意像这个例子中这样,用inline 函数表达式写引用的时候,每次有更新的时候Reac看到的都是不同的函数对象,在引用被组件的实例调用之前,引用被用null
调用。
完整的例子
var App = React.createClass({
getInitialState: function() {
return {userInput: ''};
},
handleChange: function(e) {
this.setState({userInput: e.target.value});
},
clearAndFocusInput: function() {
// 清空input
this.setState({userInput: ''}, function() {
// 当这个组件被再次渲染之后,这个代码执行
this.refs.theInput.focus(); //获取焦点了!
});
},
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
就可以访问合适的实例。这个事件对于高级的(非DOM)的组件,例如<Typeahead ref="myTypeahead" />
也适用的。
总结
在不方便通过Reactive的 props
和state
流的方式传递信息的时候,Refs是给一个特殊的子实例发送消息的重要的方法。然而,他们不应该成为你的应用程序的到哪里去的数据流的抽象。对于那些本质上来说非交互的情形来说,默认的,应该使用Reactive数据流而少用ref
。
优点:
-
在你的组件类中你可以定义任意的公开方法(例如在一个Typeahead定义一个reset方法),然后通过refs来调用这些方法(像这样
this.refs.myTypeahead.reset()
)。 -
执行 DOM操作,总是要求诸如
<input />
这样的一个“原生”组件,并通过this.refs.myInput
来访问她的低层的MOD 节点。Refs是可以做成这个事的唯一的实践可行的方法。 -
Refs自动管理!如果孩子被销毁了,它的ref也被销毁。在这里不用担心内存(除非你做了一些疯狂的事情来自己保持一个引用)。
小心:
- 绝对不要 在组件的render方法内部- 获 当任何组件的render方法还在调用栈中执行的时候,访问refs 。
- 如果你想要保护Google 闭包编译器崩溃的内力,确保绝不作为访问一个被特别的设置为一个字符串的属性。这就意味着,如果你用
ref="myRefString"
来定义你的ref的话,你必须使用this.refs['myRefsString']
来访问。 - 如果你不是用React做服务器编程的,在你的程序中,你首先的倾向是用refs来“让事情发生“。如果这是正是你的情况的话,花点时间,仔细的想想
state
在这个组件的结构层次中应在在哪里使用。通常,正确的"拥有" 状态的地方是结构层次的高层。把状态放在那些地方通常可以消除使用ref
来”让事情发生” 的渴望 - 相反的,数据流通常能完成你的目标。