Refs 和 DOM
场景:
- 访问 DOM 元素
- 访问子组件中的 DOM 元素或方法
使用方法
1. 创建 Refs
// Class 组件创建 ref
this.myRef = React.createRef()
// Function 组件创建 ref (使用 hooks 创建)
const myRef = useRef()
2. 绑定 Refs
通过 ref 属性:
- 绑定到 DOM 元素上
- 绑定到自定义 Class 组件上
- 默认不能绑定到 Function 组件上,因为它没有实例(可以使用 forwardRef 实现)
<div ref = {myRef}></div>
// MyComponent为class组件
<MyComponent ref = {myRef}/>
3. 访问 Refs
- 绑定到 React 元素上时,通过 ref 的 current 属性,访问绑定的 DOM 节点
- 绑定到 Class 组件上时,ref 的 current 属性,指向组件的挂载实例,可以访问子组件内部的方法
this.myRef.current.focus();
4. ref 创建时机
- 组件挂载时,current 属性指向绑定的 DOM 元素;卸载时指向 null。
- 在 componentDidMount 或 componentDidUpdate 触发前,React 会保证 refs 一定是最新的
5. 在父组件中引用子组件的 DOM 节点
- React 官方是不建议暴露 DOM 节点的,因为它会打破组件的封装,但它偶尔可用于触发焦点或测量子 DOM 节点的大小或位置
- 前面所说在子组件上添加 ref(class 组件)是可以访问到组件实例(以及实例中的方法),但是不能访问到子组件的 DOM 节点
- 此时可以使用 ref 转发,暴露子组件的 ref(见第二部分),或使用下面的回调 ref 也可以
↓↓↓
回调 Refs
1. 基本使用
不同于传递 createRef() 创建的 ref 属性,你会传递一个函数。这个函数中接受 React 组件实例或 HTML DOM 元素作为参数,以使它们能在其他地方被存储和访问。
class CustomTextInput extends React.Component{
constructor(props) {
super(props);
/**
* 方式1. createRef 创建 ref
*/
this.ref1 = React.createRef();
/**
* 方式2. 创建 ref 回调函数
* 搭配实例属性 textInput
* 参数为 DOM 元素或组件实例
*/
this.ref2 = (element) => {
this.textInput = element;
};
this.textInput = null;
/**
* 方式3. √
* 可以不创建 ref 回调函数
* 只需要实例属性 input
*/
this.input = null;
}
handleClick = () => {
// 方式1
this.ref1.current.focus();
// 方式2
this.textInput.focus(); // 不需要current了
// 方式3
this.input.focus(); // 不需要current了
};
render() {
return (
<div className='selectdate'>
<input type='text' ref={this.ref1} />
<input type='text' ref={this.ref2} />
<input type='text' ref={(el) => (this.input = el)} />
<input type='button' onClick={this.handleClick} value='click'/>
</div>
);
}
}
2. 在组件间传递回调形式的 refs
下面在方式 3 的基础上,进行改造~
class CustomTextInput extends React.Component{
constructor(props) {
super(props);
this.input = null;
}
handleClick = () => {
this.input.focus();
};
render() {
return (
<div className='selectdate'>
<CustomTextInput inputRef={(el) => (this.input = el)} />
<input type='button' onClick={this.handleClick} value='click'/>
</div>
);
}
}
// 子组件
function CustomTextInput(props) {
return (
<div>
<input ref={props.inputRef} />
</div>
);
}
- 理解:
- 父组件把它的 refs 回调函数当作 inputRef props 传递给子组件
- 子组件通过 props 拿到了该函数,并赋值给 ref 属性,即子组件实现了绑定回调 refs
- 所以在父组件中的 this.input 会被设置为与子组件中的 input 元素相对应的 DOM 节点。
总结一下
- 函数式子组件不能绑定 ref 。但函数式子组件用了 ref 转发,就可以了。
- class 子组件绑定 ref 后,父组件可以调用子组件内部方法,但是拿不到子组件 DOM 节点。
- 但子组件用了 ref 转发,就可以拿到了。
- 父组件使用回调 Refs,也可以拿到子组件 DOM 节点
第二部分:Refs 转发
未完~