在从 render 方法中返回 UI 结构之后,你可能想冲出 React 虚拟 DOM 的限制,在 render 返回的组件实例上调用某些方法。通常来说,这样做对于应用中的数据流动是不必要的,因为活跃的数据( Reactive data )流总是确保最新的 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 中推算出来。在这个场景中,我们想“通知” input 元素,现在应该处于获取焦点状态。然而,此处有一些障碍。从 render()
中返回的内容并不是你实际创建的子
组件的组合,仅仅是一个某一刻某个组件实例的 描述 - 一个快照。
注意:
记住,从
render()
中返回的内容并不是实际渲染出来的子组件实例。从render()
返回的仅仅是子组件层级树实例在特定时间的一个描述。
这意味着千万不要紧紧抓住 render()
返回的东西不放,然后还一厢情愿地希望生成自己想象中的东西。
// counterexample: DO NOT DO THIS!
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 支撑实例( backing instance ) 交流?
React 支持一种非常特殊的属性,你可以用来绑定到 render()
输出的任何组件上去。这个特殊的属性允许你引用 render()
返回的相应的支撑实例( backing instance )。这样就可以确保在任何时间总是拿到正确的实例。
做法很简单:
1、绑定一个 ref
属性到 render
返回的东西上面去,例如:
<input ref="myInput" />
2、在其它代码中(典型地事件处理代码),通过 this.refs
获取支撑实例( backing instance ),就像这样:
this.refs.myInput
你可以通过调用 this.refs.myInput.getDOMNode()
直接获取到组件的 DOM 节点。
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
this.refs.theInput.getDOMNode().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
获取。只要 render 返回的某个子组件带有 ref="theInput"
,this.refs.theInput
就会获取到正确的实例。这甚至对于更高层的( 非 DOM )组件生效,例如 <Typeahead ref="myTypeahead" />
。
Refs 是一种给指定的子组件实例发送消息的很好的方式,从某种程度上来看,通过 props
和 state
来做这件事倒显得不太方便。
Refs are a great way to send a message to a particular child instance in a way that would be inconvenient to do via streaming Reactive props
and state
. They should, however, not be your go-to abstraction for flowing data through your application. By default, use the Reactive data flow and save ref
s for use cases that are inherently non-reactive.
this.refs.myTypeahead.reset()
)。this.refs.myInput.getDOMNode()
获取 <input />
元素的底层 DOM 节点。 Refs 是做这件事唯一可靠的方式。this.refs['myRefString']
if your ref was defined as ref="myRefString"
.state
should be owned in the component hierarchy. Often, it becomes clear that the proper place to "own" that state is at a higher level in the hierarchy. Placing the state there often eliminates any desire to use ref
s to "make things happen" – instead, the data flow will usually accomplish your goal.