©
本文档使用
php.cn手册 发布
在典型的React数据流中,道具是父组件与子项交互的唯一方式。要修改一个孩子,你需要用新的道具重新渲染它。但是,在少数情况下,您需要在典型数据流之外强制修改子级。要修改的孩子可以是React组件的一个实例,也可以是DOM元素。对于这两种情况,React都提供了逃生舱口。
对于refs有几个很好的用例:
管理焦点,文本选择或媒体播放。
触发命令式动画。
与第三方DOM库集成。
避免将ref用于任何可以通过声明完成的事情。
例如,不要在组件上暴露open()
和使用close()
方法,而要将prop Dialog
传递isOpen
给它。
你的第一个倾向可能是使用引用来在应用程序中“发生事情”。如果是这种情况,请花一点时间,更仔细地考虑组件层次结构中应该拥有哪些状态。通常情况下,很显然,“拥有”该州的适当位置在层次结构中处于较高水平。有关这方面的示例,请参阅提升状态指南。
React支持可以附加到任何组件的特殊属性。该ref
属性采用回调函数,并且在组件挂载或卸载后立即执行回调。
当在ref
HTML元素上使用该属性时,该ref
回调接收基础DOM元素作为其参数。例如,此代码使用ref
回调来存储对DOM节点的引用:
class CustomTextInput extends React.Component { constructor(props) { super(props); this.focusTextInput = this.focusTextInput.bind(this); } focusTextInput() { // 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.focusTextInput} /> </div> ); }}
ref
当组件装入时,React将使用DOM元素调用回调,并null
在卸载时调用它。ref
回调之前被调用componentDidMount
或componentDidUpdate
生命周期钩。
使用ref
回调来设置类的属性是访问DOM元素的常用模式。首选的方法是在ref
回调中设置属性,如上例所示。甚至有更短的写法:ref={input => this.textInput = input}
。
当在ref
声明为类的自定义组件上使用该属性时,ref
回调接收组件的已装入实例作为其参数。例如,如果我们想要包装CustomTextInput
上述内容以模拟安装后立即点击它:
class AutoFocusTextInput extends React.Component { componentDidMount() { this.textInput.focusTextInput(); } render() { return ( <CustomTextInput ref={(input) => { this.textInput = input; }} /> ); } }
请注意,这仅适用于CustomTextInput
声明为类的情况:
class CustomTextInput extends React.Component { // ...}
您不能 在功能组件上使用该属性,因为它们没有实例:ref
function MyFunctionalComponent() { return <input />;} class Parent extends React.Component { render() { // This will *not* work! return ( <MyFunctionalComponent ref={(input) => { this.textInput = input; }} /> ); } }
如果需要引用组件,则应该将组件转换为类,就像在需要生命周期方法或状态时一样。
但是,只要您引用DOM元素或类组件,就可以在功能组件内使用该 属性:ref
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> ); }
在极少数情况下,您可能想要从父组件访问子节点的DOM节点。通常不建议这样做,因为它打破了组件封装,但它偶尔可用于触发焦点或测量子DOM节点的大小或位置。
虽然您可以向子组件添加ref,但这不是理想的解决方案,因为您只会获取组件实例而不是DOM节点。此外,这不适用于功能组件。
相反,在这种情况下,我们建议在儿童身上暴露特殊的道具。这个孩子会使用一个任意名称(例如inputRef
)的函数道具,并将其作为一个ref
属性附加到DOM节点。这可以让父母通过中间组件将其ref回调传递给子节点的DOM节点。
这适用于类和功能组件。
function CustomTextInput(props) { return ( <div> <input ref={props.inputRef} /> </div> );} class Parent extends React.Component { render() { return ( <CustomTextInput inputRef={el => this.inputElement = el} /> ); } }
在上面的例子中,Parent
将它的ref回调作为一个inputRef
道具传递给CustomTextInput
,并且CustomTextInput
将相同的函数作为特殊ref
属性传递给<input>
。其结果是,this.inputElement
在Parent
将被设置为对应于所述DOM节点<input>
中的元素CustomTextInput
。
请注意,inputRef
上例中prop 的名称没有特殊含义,因为它是常规组件prop。然而,使用ref
属性<input>
本身很重要,因为它告诉React将ref附加到它的DOM节点。
即使它CustomTextInput
是一个功能组件,它也可以工作。与ref
只能为DOM元素和类组件指定的特殊属性不同,对常规组件道具没有限制inputRef
。
这种模式的另一个好处是它可以深入地处理多个组件。例如,假设Parent
不需要该DOM节点,但是呈现的组件Parent
(让我们称之为Grandparent
)需要访问它。然后我们可以让Grandparent
指定的inputRef
道具Parent
,并Parent
“转发”到CustomTextInput
:
function CustomTextInput(props) { return ( <div> <input ref={props.inputRef} /> </div> ); } function Parent(props) { return ( <div> My input: <CustomTextInput inputRef={props.inputRef} /> </div> ); } class Grandparent extends React.Component { render() { return ( <Parent inputRef={el => this.inputElement = el} /> ); } }
这里,ref回调首先由指定Grandparent
。它被传递给Parent
作为一个常规道具称为inputRef
,Parent
并将其CustomTextInput
作为道具传递给它。最后,CustomTextInput
读取inputRef
prop并将传递的函数作为ref
属性附加到<input>
。其结果是,this.inputElement
在Grandparent
将被设置为对应于所述DOM节点<input>
中的元素CustomTextInput
。
所有事情都考虑到了,我们建议尽可能暴露DOM节点,但这可能是一个有用的逃生孵化器。请注意,这种方法需要您向子组件添加一些代码。如果你完全不能控制子组件的实现,你最后的选择是使用findDOMNode()
,但不鼓励。
如果你之前使用过React,那么你可能会熟悉一个较老的API,其中ref
属性是一个字符串"textInput"
,并且DOM节点被访问为this.refs.textInput
。我们建议不要这样做,因为字符串引用有一些问题,被认为是遗留问题,并且可能会在将来的某个版本中删除。如果您当前正在使用this.refs.textInput
访问参考,我们建议使用回调模式。
如果ref
回调被定义为一个内联函数,它将在更新期间被调用两次,首先null
是DOM元素,然后再一次。这是因为每个渲染都会创建一个新的函数实例,所以React需要清除旧的参考并设置新的实例。您可以通过将ref
回调定义为该类的绑定方法来避免这种情况,但请注意,在大多数情况下它不应该存在问题。