在我们刚一提到组件的时候就说过react组件有三大属性,分别是statepropsrefs。经过前面十几个课时的学习,我们也终于学完了stateprops。那么今天我们就正式开始学习最后一个组件属性refs

回顾

按照老规矩,开始这节课的内容之前要对上节课来做一个简单地回顾。

上节课我们结束了props的学习。总结下来就是props是一个只读的对象,可以批量从组件标签传入,可以利用prop-typesprops的传入进行限制,构造器尽量省略,而且函数组件可以使用props

以上就是我们前节课的内容,接下来,我们正式开始学习有关refs的课程。

refs与事件处理

其实我们学习refs的时候会伴随着事件处理一起来说,因为这二者之间的联系还挺密切,不过我们先来说一下refs

refs是什么

我们都知道refsreact组件三大属性之一,但是这个东西到底是什么呢?我们通过一个小案例来看一下:

image-20211222104441658

我们看见页面上现在有两个输入框。第一个输入框要求输入文字之后点击右侧的按钮然后弹窗提醒输入的内容,右边的第二个输入框则要求我们输入内容之后,输入框失去焦点时弹窗提示输入内容:

那么我们先一步一步来,我们先让页面上有这两个输入框和这个按钮,那么代码应该怎么编写?

我们来分析一下页面元素:

  • 输入框
  • 输入框中还有提示信息
  • 按钮

那么输入框是不是要用<input>?输入框中的提示信息不是要用placeholder?这个要点要是忘了,大家就自行回去复习HTML基础。按钮就简单了,就是<button>。那么好我们来写代码

image-20211222105127844

我们来看一下,这里为什么报红了啊?还记不记得早期课程中我们说过的所有标签都必须闭合。这里的input标签是不是没闭合那我们就给改一下:

class Demo extends React.Component {
  render() {
    return(
      <div>
        <input type="text" placeholder="点击按钮提示"/>
        <button>点击弹窗</button>
        <input type="text" placeholder="失去焦点提示"/>
      </div>
    );
  }
}
ReactDOM.render(<Demo/>, document.getElementById("test"));

大家看一下上面这段代码是不是已经对结构很熟悉了?创建类组件,类组件中必须写render方法,render方法中必须有return,必须只有一个根节点,所以用div标签封装起来。然后渲染组件到页面。好我们看一下页面:

image-20211222105724552

页面已经出来了。但是我们点击按钮好像并没有用啊,失去焦点也没用啊。那肯定没用啊,我们按钮连onClick都没给点击怎么可能有用呢?而且失去焦点的时间我们也没写肯定都是没有用的啊。那么我们就来开始吧,还是那句话别着急,一步步来,先给左侧这个组合中的按钮添加一个onClick

class Demo extends React.Component {

  showData = () => { 
    console.log("clicked!");
  }

  render() {
    return (
      <div>
        <input type="text" placeholder="点击按钮提示" />
        <button onClick={this.showData}>点击弹窗</button>
        <input type="text" placeholder="失去焦点提示" />
      </div>
    );
  }

}

我们先看一下上面这段代码,通过前面的学习我觉得大家应该能看明白吧,我给按钮加了一个onClick的时间监听,调用的方法是showData,但是我们得定义showData啊,不然报错说showData没有定义啊。那么好,根据前面的课程学习,我们是不是用赋值语句而且写箭头函数?不写箭头函数就会导致this指向丢失的问题。

但是我们的showData方法里面就只写了一行打印。我们先来验证一下这样行不行,然后再继续。

image-20211222110905901

控制台中可以看出我们的点击时间已经生效了。好了,那么我们的目的是打印这个玩意儿吗?不是啊,我们想要拿到输入框中的值啊.

但怎么拿?如果原生js的话我们可以给输入框加个id然后const i = document.getElementById(id)然后再使用alert(i.value)。这样当然是完成了我们的需求。但是我们是不是操作了真实DOM啊?我们用react,它的设计哲学是什么?各种操作都使用虚拟DOM,仅仅只在渲染页面时才操作真实DOM。那么我们在这里直接操作了真实DOM那是不是有点不讲道理?

那么我们这么写?其实react给我封装了相应的工具,那就是通过refs,之前我们给input标签id属性,现在不用了,我们要用ref属性来代替。可能有人要问,不是refs吗?你这是不是写错了?不是,就是得写ref,我们先别急往后看。

使用refs

我来问一个问题,我们这里的showData方法中的this指向的是谁?这个时候大家应该不会有任何迟疑直接就应该回答出来。这里的this指向的是不是就是Demo组件的实例对象?我们先不给inputref标签,我们先来输出一下this再来回顾一下this上有什么:

image-20211222112634406

我们发现控制台中打印的this上有一个refs我们也看见了propsrefs都是空对象。那么这俩会不会是一个性质呢?

我们在渲染组件的时候,组件标签传什么,react就会把我们传的东西收集成对象存放在props里面,那么refs会不会也一样?现在是空对象,那是因为我们什么都没传,那我们给input标签加一个ref属性试试

class Demo extends React.Component {
  showData = () => { 
    console.log(this);
  }

  render() {
    return (
      <div>
        <input ref="input1" type="text" placeholder="点击按钮提示" />
        ...
      </div>
    );
  }
}

那我们看看这次this中的refs会是什么呢?

image-20211222113236324

这次我们发现,现在的this中的refs已经有了一组key-value,而且key是我们之前传入的ref属性的值,而value是当前所处的节点,为啥我们传的是ref,而this上是refs呢?大家可能也猜到了,复数呗,refs可以收集存放多组key-value

我们看一下:

class Demo extends React.Component {
  showData = () => { 
    console.log(this);
  }
  render() {
    return (
      <div>
        <input ref="input1" type="text" placeholder="点击按钮提示" />
        <button ref="button1" type="button" onClick={this.showData}>点击弹窗</button>
        <input ref="input2" type="text" placeholder="失去焦点提示" />
      </div>
    );
  }
}

我在代码中给这几个标签都加上了ref属性,那我们再来看this

image-20211222113814325

这下refs里面是不是就有了多组key-value。这是不是和props很像啊?那我们就继续往下写。

class Demo extends React.Component {
  showData = () => { 
    console.log(this.refs.input1);
  }
  render() {
    return (
      <div>
        <input ref="input1" type="text" placeholder="点击按钮提示" />
        <button type="button" onClick={this.showData}>点击弹窗</button>
        <input type="text" placeholder="失去焦点提示" />
      </div>
    );
  }
}

我们现在是不是还不打算给其他两个标签加ref?我们目前先实现点击事件,失去焦点事件后面再说。而且从始至终是不是都不需要给按钮加ref?那么我们收集到了ref存放到refs中之后,我们怎么用?refs是不是在this上?那么this.refs能不能访问到?肯定可以的。然后refs是不是一个对象?那么不就好办了嘛。这个是不是和props几乎没有什么区别?那我们打印一下我们取到的是什么

image-20211222130417756

我们看见这是不是我们写的input标签?但是为什么这个标签里面没有ref属性啊?我们回想一下HTML的标签里面有ref属性吗?从来就没有啊。ref属性只是给react用的一个标识。所以说我们给这个input标签加了ref标识为input1,那么我们用this.refs.input1获取到的就是这个标签所在的节点。

但是又有人要问了,那我们这里拿到的是不是虚拟DOM啊?当然不是,我们在讲虚拟DOM的时候就说过,虚拟DOM只在react内部使用,是我们开发人员拿不到的。这里其实是虚拟DOM转成真实DOM的一个input标签所在的节点。也可以说这是一个真实DOM

那么我们接下来就简单了,我们都知道这是个真实DOM了,那么我们按照真实DOM的操作方法来做就行了啊:

class Demo extends React.Component {
      showData = () => { 
        alert(this.refs.input1.value);
      }
      render() {return ...}
    }

那我们来看一下效果:

image-20211222131928304

这下我就完成了点击按钮来弹窗展示输入框输入的值的功能了。

那么右侧的失去焦点弹窗展示输入框内容是不是就以此类推一下就可以了?

或许有人说失去焦点我们没写过啊,原生js中失去焦点是不是onblur?在react中是啥?我们都知道onclickreact中要把Click的首字母大写,那么onblur依葫芦画瓢呗:

class Demo extends React.Component {
  showData = () => {...}

  blurData = () => {
    alert(this.refs.input2.value);
  }

  render() {
    return (
      <div>
        <input ref="input1" type="text" placeholder="点击按钮提示" />
        <button type="button" onClick={this.showData}>点击弹窗</button>
        <input ref="input2" onBlur={this.blurData} type="text" 
          placeholder="失去焦点提示" />
      </div>
    );
  }
}

我们看一下,onBlur绑定一个方法blurData,然后定义方法,在方法里面弹窗提醒this.refs.input2.value,这样是不是就取到了ref="input2"的节点,然后取到数据,弹窗展示出来。那我们来看一下具体效果:

image-20211222132844440

这样,我们就完成了右侧输入框失去焦点自动弹窗展示输入框中内容的功能。

以上就是我们这节课的内容

总结

  • refs是一个收集各个标签ref属性来作为标识的一个对象
  • refs可以代替document.getElementById方法来获取页面节点
  • 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/string_refs/