React--10: 组件的三大核心属性3:refs与事件处理

1. 字符串形式的ref

首先这种形式是不推荐使用的。

过时 API:String 类型的 Refs:

如果你之前使用过 React,你可能了解过之前的 API 中的 string 类型的 ref 属性,例如 "textInput"。你可以通过 this.refs.textInput 来访问 DOM 节点。我们不建议使用它,因为 string 类型的 refs 存在一些效率上的问题。它已过时并可能会在未来的版本被移除(16.8版本还没有移除)。

image.png

点击按钮获取输入框数据

按照我们原生的写法,怎么在函数中获得输入框中的内容呢?首先给输入框一个id,然后通过getElementById 获得输入框中的值。

class Demo extends React.Component{
    showData = ()=>{
        let value = document.getElementById('input1').value
        console.log(value)
    }   
    render(){
        return(
            <div>
                <input  type="text" placeholder="点击按钮提示数据"/>&nbsp;
                <button onClick={this.showData}>点击提示数据</button>&nbsp;
                <input type="text" placeholder="失去焦点提示数据" />&nbsp;
            </div>
        )
    }
}

但是在React中去使用原生不是很好。因此ref就出现了。给input标签中添加ref属性(就类似于id)

image.png

此时输出的this是类的实例 。 我们发现了refs中有 input1,是键值对类型。

image.png

打印、获取输入框的内容

    showData = ()=>{
       console.log(this.refs.input1.value)
       const {input1} = this.refs
    }   

失去焦点提示数据

<input type="text"  ref="input2" 
onBlur={this.showData2}  placeholder="失去焦点提示数据" />&nbsp;
  showData2 =()=>{
        const {input2} = this.refs
        alert(input2.value)
    }

image.png

总结

refs 是实例上的属性。ref就像原生js的id,可以理解为打标签。

import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import './index.css';
class Demo extends React.Component{
    showData = ()=>{
       console.log(this.refs.input1.value)
    }   
    showData2 =()=>{
        const {input2} = this.refs
        alert(input2.value)
    }
    render(){
        return(
            <div>
                <input ref="input1" type="text" placeholder="点击按钮提示数据"/>&nbsp;
                <button onClick={this.showData}>点击提示数据</button>&nbsp;
                <input type="text"  ref="input2" onBlur={this.showData2}  placeholder="失去焦点提示数据" />&nbsp;
            </div>
        )
    }
}
ReactDOM.render(<Demo/>, document.getElementById('root'))

2. 回调形似的ref

ref 中写回调函数,传入的参数是什么呢?我们打印看一下。

  <input ref={(a)=>{console.log(a)}} type="text" placeholder="点击按钮提示数据"/>

可以看到打印出来的是ref所处节点

image.png
我们接下来把ref所处节点挂载到实例自身上,并取了个名字input1(剪头函数的 this 是其外部的 this,也就是render的实例,也就是 Demo实例)
image.png

完整代码:

class Demo extends React.Component{
    showData = ()=>{
       const {input1} = this
       alert(input1.value)
    }   
    showData2 =()=>{
        console.log(this)
        const {input2} = this
        alert(input2.value)
    }
    render(){
        return(
            <div>
                <input ref={(a)=>{this.input1 = a}} type="text" placeholder="点击按钮提示数据"/>&nbsp;
                <button onClick={this.showData}>点击提示数据</button>&nbsp;
                {/* 剪头函数只有一个参数的时候可以简写 */}
                <input type="text"  ref={c=>this.input2=c} onBlur={this.showData2}  placeholder="失去焦点提示数据" />&nbsp;
            </div>
        )
    }
}

回调执行次数问题

关于回调 refs 的说明

如果 ref 回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的。

内联的写法

首先什么是内联函数?如下ref中的函数就是内联函数。

   <input ref={(currentNode)=>{this.input1 = currentNode;console.log("currentNode",currentNode)}} type="text" placeholder="点击按钮提示数据"/>&nbsp;

那么什么又算更新过程呢?

我点击按钮输出文本框的内容算吗?这只是交互,并不算是更新。

还记得我们前几篇文章用到的点击按钮切换天气的例子吗?我们在这里再次用到它。也就是用setState的使用。

    state = {
        isHot:true
    }
    showInfo = ()=>{
      const {input1} = this
      alert(input1.value)
    }
    changeWeather = ()=>{
        // 获取原来状态
        const {isHot} = this.state
        this.setState({isHot:!isHot})
    }
    render(){
        const {isHot} = this.state
        return(
            <div>
                <h1>今天天气很{isHot?"炎热":"凉爽"}</h1>
                <input ref={(currentNode)=>{this.input1 = currentNode;console.log("currentNode",currentNode)}} type="text" placeholder="点击按钮提示数据"/><br></br>
                <button onClick={this.showInfo}>点击提示数据</button><br></br>
                <button onClick={this.changeWeather}>点击改变天气</button><br></br>
            </div>
        )
    }

点击改变天气按钮 我们发现 打印了两次,并且第一次是null,第二次才是节点。(点击改变天气使页面进行了更新)
image.png

🤔

当更新页面时,render方法就会被调用一次。然后<input ref={(currentNode)=>{this.input1 = currentNode;console.log("currentNode",currentNode)}} type="text" placeholder="点击按钮提示数据"/>代码就会执行,它又会发现ref,而且还是函数式的ref。这个函数又是一个新的函数了,之前的函数被执行完释放了。它并不确定之前的函数执行了什么,因此为了清空上一次调用的函数,传了null将第函数清空,第二次才把当前节点传进来。

怎么解决呢?🤔

class 的绑定函数的写法

通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的。 this.saveInput

class Demo extends React.Component{
    state = {
        isHot:true
    }
    showInfo = ()=>{
      const {input1} = this
      alert(input1.value)
    }
    changeWeather = ()=>{
        // 获取原来状态
        const {isHot} = this.state
        this.setState({isHot:!isHot})
    }
    saveInput = (c)=>{
        this.input1 = c
        console.log("c",c)
    }
    render(){
        const {isHot} = this.state
        return(
            <div>
                <h1>今天天气很{isHot?"炎热":"凉爽"}</h1>
                {/* <input ref={(currentNode)=>{this.input1 = currentNode;console.log("currentNode",currentNode)}} type="text" placeholder="点击按钮提示数据"/><br></br> */}
                <input ref={this.saveInput} type="text" placeholder="点击按钮提示数据"/><br></br> 
                <button onClick={this.showInfo}>点击提示数据</button><br></br>
                <button onClick={this.changeWeather}>点击改变天气</button><br></br>
            </div>
        )
    }
}

现在怎么点击都不会频繁的调用 saveInput 了,因为 saveInput已经放在实例自身了。

当然直接写成内联的也问题不太。内联的写法是比较常见的。

3. CreateRef

使用 createRef API

React.createRef调用后可以返回一个容器,该容器可以存储被ref标识的节点。但是只能存放一个

class Demo extends React.Component{
    // React.createRef调用后可以返回一个容器,该容器可以存储被ref标识的节点
    myRef = React.createRef()
    state = {
        isHot:true
    }
    showData = ()=>{
        console.log(this.myRef)
    }
    render(){
        const {isHot} = this.state
        return(
            <div>
                <h1>今天天气很{isHot?"炎热":"凉爽"}</h1>
                <input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/><br></br> 
                <button onClick={this.showData}>点击按钮提示数据</button><br></br>
            </div>
        )
    }
}

打印 myRef

image.png

获得节点对应的值

showData = ()=>{
        console.log(this.myRef.current.value)
    }

但是这种容器,只能存一个。如果有多个节点,那只能声明多个myRef。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值