第二部分:React进阶
系列文章目录
第一章:React从入门到进阶之初识React
第一章:React从入门到进阶之JSX简介
第三章:React从入门到进阶之元素渲染
第四章:React从入门到进阶之JSX虚拟DOM渲染为真实DOM的原理和步骤
第五章:React从入门到进阶之组件化开发及Props属性传值
第六章:React从入门到进阶之state及组件的生命周期
第七章:React从入门到进阶之React事件处理
第八章:React从入门到进阶之React条件渲染
第九章:React从入门到进阶之React中的列表与key
第十章:React从入门到进阶之表单及受控组件和非受控组件
第十一章:React从入门到进阶之组件的状态提升
第十二章:React从入门到进阶之组件的组合使用
第十三章:React从入门到进阶之组件的组件的懒加载及上下文Context
第十四章:React从入门到进阶之Refs&DOM以及Refs转发
什么是Refs
Refs提供了一种方式,运许我们访问DOM节点或在render方法中创建的React元素
在典型的React数据流中,props是父组件与子组件交互的唯一方式。要想修改一个子组件,我们需要使用新的props来重新渲染。但是在某些情况下,我们不得不在典型数据流之外去修改一些子组件。被修改的子组件可能是一个React组件的实例,也可能是一个DOM元素。这种情况下我们就必须要考虑其它方式了。好在React为我们提供了解决办法。
什么时候使用Refs
以下几种情况适合使用refs:
- 管理焦点,文本选择或媒体播放
- 触发强制动画
- 集成第三方DOM库
refs虽然能够让我们去操作DOM元素,但是在实际开发中不要过度使用refs,也就是说能不用refs的就尽量不要使用
Refs的使用
- 创建Refs
Refs 是用React.createRef()来创建的,并通过 ref 属性附加到 React 元素。在构造组件时,通常将 Refs 分配给实例属性,以便可以在整个组件中引用它们。
class RefComponent extends React.Component{
constructor(props){
super(props);
this.myRef = React.createRef();
}
render(){
return <div ref={
this.myRef} />
}
}
- 访问Refs
访问ref的方式也很简单,当ref被传递给render中的元素时,对该节点的引用可以在 ref 的 current 属性中被访问。也就是说我们创建的ref会有一个current属性,那么我们所要操作的元素就存在这个current中。
ref的值根据节点的类型有所不同:
- 当ref属性用于HTML元素时,构造函数中使用React.createRef()创建的ref接收底层DOM元素作为其current属性。
- 当ref属性用于自定义Class组件时,ref对象接收组件的挂载实例作为其current属性。
- **注意:**我们不能在函数组件上使用ref属性,因为它们没有实例
下面来看两个案例:
- 为DOM元素添加ref
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
//创建一个ref来存储DOM元素
this.textInput = React.createRef();
}
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>
);
}
}
React 会在组件挂载时给 current 属性传入 DOM 元素,并在组件卸载时传入 null 值。ref 会在 componentDidMount 或 componentDidUpdate 生命周期钩子触发前更新。
- 为 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} />
);
}
}
class CustomTextInput extends React.Component {
// ...
}
需要注意的是:仅在 CustomTextInput 声明为 class 时才有效
Refs 与函数组件
默认情况下,我们不能在函数组件上使用 ref 属性,因为它们没有实例。也就是说我们自己定义的函数组件,当别人引用它并且想使用ref属性,这种是无效的。
如果非要在函数组件中使用 ref,可以使用 forwardRef,或者将该组件转化为 class 组件。
function MyFunctionComponent() {
return <input />;
}
class App extends React.Component{
constructor(props){
super(porps);
this.textInput = React.createRef();
}
render(){
//ref是无效的
return <MyFunctionComponent ref={
this.textInput} />
}
}
虽然不能在函数组件上使用ref,但是我们可以在函数组件内部使用ref,需要借助useRef() 钩子函数
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>
);
}
Refs转发
Ref 转发是一项将 ref 自动地通过组件传递到其一子组件的技巧。对于大多数应用中的组件来说,这通常不是必需的。但其对某些组件,尤其是可重用的组件库是很有用的。
下面的示例中,MyButton使用React.forwardRef来获取传递给它的ref,然后转发到它渲染的DOM button上。这样使用MyButton的组件可以获取到底层DOM节点button的ref,并在必要时访问,就像直接使用DOM button一样
const MyButton = React.forwardRef((props, ref) =>{
(
<button ref={
ref} className="my-button">{
props.children}</button>
)
});
//可以直接获取DOM button的ref
const ref = React.createRef();
<MyButton ref={
ref}>click me</MyButton>
- 上面说过,在函数组件上是不能使用ref的,那么这里却使用了ref那么是不是自相矛盾了呢?其实并不矛盾,细想:当我们在类组件或原生DOM元素上使用ref属性时,那么这个ref是直接与类组件或DOM元素绑定的,也就是说我们可以通过ref就能直接操作组件或者DOM元素。
- 而在这个示例中我们在函数组件上使用了ref,但不同的是:这个ref绑定的并不是当前函数组件本身,而是函数组件的子元素button
下面简单简单描述一下上面案例的执行步骤:
- 1、我们通过调用React.createRef()创建了一个ref并将其赋值给变量ref
- 2、指定ref为JSX属性,通过MyButton组件将其向下传递
- 3、React 将ref作为参数传递给forwardRef函数,再通过该函数将其向下传递
- 4、然后向下转发该ref参数到button上,同时将其指定为button的JSX属性
- 5、当 ref 挂载完成,ref.current 将指向 < button> DOM 节点。
- 6、最后我们就可以在外部直接访问到组件内部的元素了
以上就是关于Refs的一些相关知识。
下一章节中我们将会继续学习React进阶中的高阶组件