在 React 组件中,你可以创建 ref 对象使其指向一个子组件的实例或者 DOM 元素,这样你可以显式地调用它的 API 来改变它(而不是通过指令式的 props 和 state)。在 React: Concept: Form Elements 提到的 uncontrolled components 即使用了 ref。
什么情况下可以用 refs?
大多数情况下你都不应该使用 ref,因为它是命令式的(即你会调用实际的 API 去改变组件状态),而不是通过 props / state 这种指令式让 React 帮你做改变。仅在这些情况下使用它:
- 有一些 DOM 元素的操作,无法通过 React 的指令式来改变时,比如操作
<input>
元素的 focus 状态、选中文本,或者控制媒体播放 - 调用命令式的动画
- 与第三方 DOM 库结合
这个例子表示什么样的过程是命令式和声明式。比如你有一个对话框组件,它默认是隐藏的,一旦你点击了开启的按钮,它会显示出来。那么:
- 调用这个组件的
open()
和close()
函数来控制是否显示,即是 命令式 的 - 设计一个
isOpened
变量,由 React 判断这个变量来决定是否展示对话框,即是 声明式 的
基本用法
在 class component 中使用 React.createRef()
:
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
// create a ref to store the textInput DOM element
this.textInput = React.createRef();
this.focusTextInput = this.focusTextInput.bind(this);
}
focusTextInput() {
// Explicitly focus the text input using the raw DOM API
// Note: we're accessing "current" to get the DOM node
this.textInput.current.focus();
}
render() {
// tell React that we want to associate the <input> ref
// with the `textInput` that we created in the constructor
return (
<div>
<input
type="text"
ref={this.textInput} />
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}
在 functional component 中,使用 useRef
:
function CustomTextInput(props) {
// textInput must be declared here so the ref can refer to it
const textInput = useRef(null);
function handleClick() {
textInput.current.focus();
}
return (
<div>
<input
type="text"
ref={textInput} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
}
Callback Refs
如果你想更好的地控制 ref 的生命周期,可以用 callback refs:
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = null;
this.setTextInputRef = element => {
this.textInput = element;
};
this.focusTextInput = () => {
// Focus the text input using the raw DOM API
if (this.textInput) this.textInput.focus();
};
}
componentDidMount() {
// autofocus the input on mount
this.focusTextInput();
}
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={this.setTextInputRef}
/>
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}
它的重点是,将 this.setTextInputRef
传至 <input type="text" ref={this.setTextInputRef} />
。React 保证:
- 在
componentDidMount
/componentDidUpdate
前,以实际的<input>
node 作为参数调用setTextInputRef
。这样this.textInput
便指向了 DOM 节点 - 在
componentDidUnmount
时(未提及前后),调用setTextInputRef(null)
,从而将this.textInput
设置为了 null
除了更好地控制 this.textInput
变量之外,我翻了一些博客文章,并没有谁提到这个能力能应用在什么地方。也许适合在 unmount 时做一些清洁工作?
传递 ref 给一个组件
非典型的使用场景。参考 React 官方文档 以及 Exposing DOM Refs to Parent Components 一节。