React版本:15.4.2
**翻译:xiyoki **
在典型的React数据流中,props是父组件与其children交互的唯一方式。要修改child,你需使用新的props重新渲染父组件。但是,在一些情况下,你需要在典型的数据流之外强制修改child。要修改的child可以是React组件的实例,也可以是DOM元素。对于这两种情况,React提供了一个 escape hatch(安全舱口; 逃口)。
When to Use Refs
几个好的refs使用案例:
- 管理焦点、文本选择框或媒体重放。
- 触发命令式动画。
- 与第三方DOM库集成。
避免在可声明的事物上使用refs。
例如,不在Dialog
组件上暴露open()
和close()
方法,而是向它传递isOpen
属性。
Adding a Ref to a DOM Element
React支持你附加一个特殊的属性到任何组件。该ref
属性接收一个回调函数作为参数,在组件被mounted或unmounted后,该回调就被立即执行。
当在HTML元素上使用ref
属性时,ref
回调接收基础DOM元素作为参数。例如,此代码使用ref
回调来储存对DOM节点的引用:
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.focus = this.focus.bind(this);
}
focus() {
// Explicitly focus the text input using the raw DOM API
this.textInput.focus();
}
render() {
// Use the `ref` callback to store a reference to the text input DOM
// element in an instance field (for example, this.textInput).
return (
<div>
<input
type="text"
ref={(input) => { this.textInput = input; }} />
<input
type="button"
value="Focus the text input"
onClick={this.focus}
/>
</div>
);
}
}
当组件mounts时,React会调用ref
回调,并向其传入DOM元素。当组件unmounts时,React会调用ref
回调,并向其传入null。
使用ref
回调为类设置一个属性,是访问DOM元素的常见模式。首选方法是在ref
回调中设置属性,就像上面的例子。甚至有一个简便的书写方式:ref={input => this.textInput = input}
。
Adding a Ref to a Class Component
当ref
属性被用在自定义类组件上时,ref
回调接收已mounted的组件实例作为参数。例如,如果我们想在CustomTextInput
上包裹一圈,模拟组件mounting后,它直接被点击:
class AutoFocusTextInput extends React.Component {
componentDidMount() {
this.textInput.focus();
}
render() {
return (
<CustomTextInput
ref={(input) => { this.textInput = input; }} />
);
}
}
注意,只有CustomTextInput
被声明为类时才有效:
class CustomTextInput extends React.Component {
// ...
}
Refs and Functional Components
你不能(在引用功能组件时)在功能组件上使用ref
属性,因为功能组件没有实例:
function MyFunctionalComponent() {
return <input />;
}
class Parent extends React.Component {
render() {
// This will *not* work!
return (
<MyFunctionalComponent
ref={(input) => { this.textInput = input; }} />
);
}
}
如果你需要一个ref,你应该将组件转换为一个类,就像你需要声明周期方法或状态时一样。
但你可以在功能组件内部使用ref
属性,只要是涉及DOM元素或类组件:
function CustomTextInput(props) {
// textInput must be declared here so the ref callback can refer to it
let textInput = null;
function handleClick() {
textInput.focus();
}
return (
<div>
<input
type="text"
ref={(input) => { textInput = input; }} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
}
Don't Overuse Refs(不要过度使用Refs)
你使用refs的第一个倾向可能是让事情在你的应用程序中发生。如果是这种情况,请花一点时间,着重考虑状态应该在组件层次结构的什么位置。通常,拥有该状态的适当位置在组件层次结构中的层级更高。
Legacy API: String Refs(旧版API:字符串Refs)
如果你之前使用过React,你可能会熟悉一个较旧的API,其中ref
属性是一个字符串,如像"textInput"
,DOM节点被这样访问this.refs.textInput
。我们建议不这样使用。因为字符串引用有一些被认为是遗留的问题,并且可能在未来的一个版本中被删除。如果你目前正在使用this.refs.textInput
访问refs,我们建议你使用回调模式。
Caveats(注意)
如果ref
回调被定义为内联函数,它将在更新期间被调用两次,第一次调用传入null,第二次调用传入DOM 元素。这是因为函数的新实例是通过每次渲染创建的,因此React需要清除旧的引用并设置新的引用。你可以通过将ref
回调定义为类上的绑定方法来避免这种情况,但请注意,在大多数情况下它不重要。