本篇文章是对文档的自我学习,都已经完全掌握的同学也已不必去看。
Refs
Refs 提供了我们能够访问DOM节点的能力,或者在 render 方法中创建的 React 元素。
我们知道 React 是单向数据流,props 是父组件与子组件进行交互的唯一方式。如果想要修改一个子组件,你需要用props来重新渲染他。
但是在某些情况下,你需要在 React 数据流之外强制修改子组件。被修改的组件可能是 React 组件的实例,也可能是一个DOM元素。
何时使用 Refs
下面几种情况适合使用refs:
- 管理焦点,文本选择或者媒体播放
- 触发强制动画
- 集成第三方 DOM 库
避免使用 refs 来使用子组件中的方法,比如:子组件中的方法通过refs方式被父组件调用。
创建 refs
Refs是使用 React.createRef() 创建的,并通过 ref 属性附加到React元素。
在构造组件时,通常将Refs分配给实例属性,以便可以在整个组件中引用他们。
class MyComponent extends React.Component {
constructor(props) {
super(props)
this.myRef = React.createRef()
}
render() {
return <div ref={this.myRef} />
}
}
访问 refs
当 ref 被传递给 render中的元素时,对该节点的引用可以在ref的current属性中访问。
const node = this.myRef.current;
ref的值根据节点的类型而有所不同:
- 当ref属性用于HTML元素时,构造函数中使用React.createRef()创建的ref接收底层DOM元素作为其current属性。
- 当ref属性用于定义class组件时,ref对象接收组件的挂载实例作为其current属性。
- 你不能在函数组件上使用ref属性,因为他们没有实例。
下面的🌰将说明这些差异:
为DOM元素添加ref
以下代码使用ref去存储DOM节点的引用:
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
// 创建一个 ref 来存储 textInput 的 DOM 元素
this.textInput = React.createRef();
this.focusTextInput = this.focusTextInput.bind(this);
}
focusTextInput() {
// 直接使用原生 API 使 text 输入框获得焦点
// 注意:我们通过 "current" 来访问 DOM 节点
this.textInput.current.focus();
}
render() {
// 告诉 React 我们想把 <input> ref 关联到
// 构造器里创建的 `textInput` 上
return (
<div>
<input
type="text"
ref={this.textInput} />
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput} />
</div>
)
}
}
上面的代码,当onClick事件被触发时会使得第一个input获取焦点,事件中通过ref触发获取焦点。
React会在组件挂载时给current属性传入DOM元素,并在组件卸载时传入null的值。Ref会在componentDidMount或compoonentDidUpdate触发之前进行更新。
为class组件添加ref
如果我们想包装上面的CustomTextInput组件,来模拟他挂载之后立即被点击的操作,我们可以使用ref来获取这个自定义的input组件,并手动调用他的focusTextInput方法:
class AutoFocusTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef()
}
componentDidMount() {
this.textInput.current.focusTextInput()
}
render() {
return (
<CustomTextInput ref={this.textInput} />
)
}
}
只有在CustomTextInput为class组件时才能够这样使用ref,接下来看看函数组件如何使用。
Refs 与函数组件
默认情况下,你不能在函数组件上使用 ref 属性,因为没有实例:
function MyFunctionComponent() {
return <input />
}
class Parent extends React.Component {
constructor(props) {
super(props)
this.textInput = React.createRef()
}
render() {
// This will *not* work!
return (
<MyFunctionComponent ref={this.textInput} />
)
}
}
如果要在函数组件中使用 ref,你可以使用 forwardRef。
function CustomTextInput(props) {
// 这里必须声明 textInput,这样 ref 才可以引用它
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>
)
}
useRef 在之后讲到Hooks在进行详解。
回调 Refs
我们只来看看函数组件中如何进行回调的。
function CustomTextInput(props) {
return (
<div>
<input ref={props.inputRef} />
</div>
)
}
class Parent extends React.Component {
render() {
return (
<CustomTextInput
inputRef={el => this.inputElement = el} />
)
}
}
在上面的例子中,Parent
把它的 refs 回调函数当作 inputRef
props 传递给了 CustomTextInput
,而且 CustomTextInput
把相同的函数作为特殊的 ref
属性传递给了 <input>
。
结果是,在 Parent
中的 this.inputElement
会被设置为与 CustomTextInput
中的 input
元素相对应的 DOM 节点。
总结
- refs 如何创建,并且如何访问的
- refs 能够通过回调的方式设置