上节课我们开始了react组件三大属性的最后一个属性refs的学习,上节课呢我们学习的是字符串类型的ref,以及ref在事件处理方面的应用。但是ref其实并不仅仅只有字符串形式。

那么今天,我们来学习一下关于回调形式的ref的运用

回顾

  • refs是一个收集各个标签ref属性来作为标识的一个对象
  • refs可以代替document.getElementById方法来获取页面节点
  • refs可以配合事件处理来完成相应的功能需求。

以上便是上节课的知识要点,接下来我们开始正式学习回调形式的ref

概述

上节课中我们学习的是字符串类型的ref,但是有一点其实官方呢已经不推荐使用字符串类型的ref了,而且官方明确表示,在后期的新版本更新中可能将把字符串形式的ref就废弃掉了。或许大家会说,那我上节课学了那么半天不是玩儿呢嘛。咱别这么说,万一咱接手了一个旧版本的项目那还是得会这个的。而且面试的时候相关的知识也得用啊。准我们不用,但是你不能不会啊。

但是为啥不不建议使用字符串类型的ref呢?我们来看一下官方文档:

image-20211222141056186

官方文档告诉我们,字符串类型的refs存在一些问题。具体是什么问题?我们点击这个链接看一下:

image-20211222141443574

可能有人会说我看不懂。这是直接跳转到GitHub上了,我们这里也不去详细解释了,就简单概括一下,就是说,字符串类型的refs在效率方面有问题。也就是说如果字符串类型的refs用多了,那么就会拖慢整个项目的运行效率。所以说react官方已经不推荐使用字符串类型的refs

回调形式的refs

那行吧,既然官方不推荐了,那就不用呗,那我们用什么呢?官方其实推荐我们使用回调形式或者creatRef API

但是回调形式的refs应该怎么用呢?我们在代码中分析一下:

class Demo extends React.Component {
  render() {
    return (
      <div>
        <input type="text" placeholder="click the button" />
        <button onClick={this.showData}>Click me</button>
        <input onBlur={this.bulrData} type="text" placeholder="lose focus" />
      </div>
    );
  }
  showData = () => { }
  bulrData = () => { }
}

我们看上面这段代码,我们现在都还没有加ref属性,所以说我们目前也不知道该怎么编写showDatablurData这两个方法,先不着急,我们说要写回调形式的ref是什么意思呢?没有什么绕弯子的意思,很直白,就是ref是一个回调函数呗,那么我们先给其中一个input标签加一个ref吧:

class Demo extends React.Component {
  render() {
    return (
      <div>
        <input ref={() => {}} type="text" placeholder="click the button" />
        ...
      </div>
    );
  }
  ...
}

上面这段代码我们给ref设定的值是一个箭头函数。但是这个是回调函数吗?怎么判断一个函数是不是回调函数?是不是有三个特点?

  1. 我们自定义的函数
  2. 我们自己没有调用
  3. 最终函数被执行了

那我们看一下,这是我们定义的,而且我们定义之后确实没有调用,但是这个函数最终执行了吗?那我们打印一下看看:

class Demo extends React.Component {
  render() {
    return (
      <div>
        <input ref={() => {console.log("call the func");}} type="text" 
          placeholder="click the button" />
        ...
      </div>
    );
  }
  ...
}

看一下效果吧:

image-20211222144403442

控制台有输出,看来是成功调用了。那么这个不用说就是一个回调函数。但是这个回调函数会收到什么参数呢?这个是不是取决于这个回调函数的调用者?而且这个回调函数到底接没接到参数?就算接到了,接到的是什么啊?那我们再打印一下看看就知道了啊

class Demo extends React.Component {
  render() {
    return (
      <div>
        <input ref={(a) => {console.log(a);}} type="text" 
          placeholder="click the button" />
        ...
      </div>
    );
  }
  ...
}

我们用a来接一下参数然后把参数打印出来:

image-20211222144845905

诶,我们看到了我们所接到的就是这个这个加了ref属性的标签节点。

然后大家又开始发散思维了,我们之前字符串的refs调用的时候this.refs.input1得到的就是这个ref="input1"所在的标签,那么我们这里的回调函数里面是不是return a就行了呢?

我们来看一下:

class Demo extends React.Component {
  render() {
    return (
      <div>
        <input ref={(a) => {return a;}} type="text" placeholder="click the button" />
        <button onClick={this.showData}>Click me</button>
        <input onBlur={this.bulrData} type="text" placeholder="lose focus" />
      </div>
    );
  }
  showData = () => { console.log(this); }
  bulrData = () => { }
}

我们先来在点击事件里面打印一下this看看:

image-20211222145520400

这是为什么?为什么我们在回调函数里面返回了接到的参数refs却没有收集呢?这个收集是不是我们在字符串ref那一节说的?这里不是字符串ref啊,那肯定就不支持这种方法了。而且跟你有没有return没有关系,因为这个是定义一个回调函数又不是定义一个函数并获取返回值。

退一万步来说:ref={ () => {} },这是不是相当于ref=function,按照字符串ref的规则,是不是应该收集function: DOM然后存放到this.refs里面?这是一对key-value,但是大家有谁见过拿一个函数做key的?

image-20211222165156024

大家看这是不是报错了?所以说只要ref属性不是字符串,那就默认不收集到this.refs

那我们来说一下这个回调函数应该怎么写:

class Demo extends React.Component {
  render() {
    return (
      <div>
        <input ref={(a) => {this.input1 = a;}} type="text" 
          placeholder="click the button" />
        ...
      </div>
    );
  }
  showData = () => { }
  bulrData = () => { }
}

我们在回调函数中把a赋值给this.input1但是这是什么意思呢?react要想渲染页面就得实例化我们的Demo组件,然后再通过Demo的实例来调用render,那调用render方法就一定会执行render方法中的jsx代码,那么就一定会触发ref的这个回调函数的执行。

在看我们这个回调函数,代码层面上说,我们的目的是不是只要一执行就把传进去的标签节点放到实例自身的input1属性上?

但是,我来问大家,这个回调函数是谁调的?我们是不是从始至终都不太确定啊?我们只是知道了这个回调函数接到了什么参数,至于这个参数是谁传的?不确定,那这个回调函数有this吗?

state的简写那一节我们就说过箭头函数没有自己的this,但是如果在箭头函数中使用this关键字的话,会自动往箭头函数的外层去寻找,那么这里的this指向是什么?是不是组件的实例对象。以为这个回调函数的外层是render方法,render方法是react通过Demo组件的实例调用的,那么render方法中的this指向的就是组件的实例对象啊。

也就是说我们其实是达到了我们的目的了,我们确实成功地将标签节点放在了实例自身的input1属性上。那么我们在改一下代码:

class Demo extends React.Component {
  render() {
    return (
      <div>
        <input ref={c => this.input1 = a;} type="text" 
          placeholder="click the button" />
        ...
      </div>
    );
  }
  showData = () => { alert(this.input1.value); }
  bulrData = () => { }
}

这下我们不从refs里面取value了,我们直接从this.input1上取。看一下结果:

image-20211222162730815

一切正常。那么另一个标签是不是也就照方抓药?

class Demo extends React.Component {
  render() {
    return (
      <div>
        <input ref={c => this.input1 = c} type="text" placeholder="click the button" />
        <button onClick={this.showData}>Click me</button>
        <input ref={c => this.input2 = c} onBlur={this.bulrData} type="text" 
          placeholder="lose focus" />
      </div>
    );
  }
  showData = () => { alert(this.input1.value); };
  bulrData = () => { alert(this.input2.value); }
}

再看一下效果:

image-20211222163054009

我们看一下,是不是也正常实现功能了啊?

以上便是这节课的主要内容。

总结

  • 回调式refs不会自动收集到this.refs
  • 字符串式refs有问题,后期可能会废弃
  • 回调式refs所传入的ref属性是一个回调函数
  • 回调式refs是将传入的标签节点传入回调函数,并通过回调函数将标签节点挂在实例自身

Copyright statement:The articles of this site are all original if there is no special explanation, indicate the source please when you reprint.

Link of this article:https://work.lynchow.com/article/callback_refs/