web前端高级React - React从入门到进阶之Refs&DOM以及Refs转发

第二部分: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进阶中的高阶组件

猜你喜欢

转载自blog.csdn.net/lixiaosenlin/article/details/112969877