小胖墩er 小胖墩er
首页
  • 前端文章

    • JavaScript
    • Vue
    • ES6
    • Git
  • Vue
  • React
  • HTML
  • CSS
  • 工具类
  • GitHub技巧
  • 博客搭建
  • 友情链接
💖关于
💻收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

小胖墩er

Better later than never.
首页
  • 前端文章

    • JavaScript
    • Vue
    • ES6
    • Git
  • Vue
  • React
  • HTML
  • CSS
  • 工具类
  • GitHub技巧
  • 博客搭建
  • 友情链接
💖关于
💻收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 基本知识

    • React基础
    • React组件三大核心属性 - State
    • React组件三大核心属性 - Props
    • React组件三大核心属性 - Refs
  • Hooks

  • React17新版本概念

  • React笔记
  • 基本知识
小胖墩er
2021-10-22

React组件三大核心属性 - Refs

# 一、refs的理解

组件内的标签可以定义ref属性来标识自己,就比如原生中通过document获取dom元素的方法,这里ref获取的是虚拟DOM转换为真实的DOM,而不是获取虚拟DOM。

此属性可以是一个由 React.createRef() 函数 (opens new window)创建的对象、或者一个回调函数、或者一个字符串(遗留 API)。当 ref 属性是一个回调函数时,此函数会(根据元素的类型)接收底层 DOM 元素或 class 实例作为其参数。这能够让你直接访问 DOM 元素或组件实例。

为什么是Refs?

refs存放的是所有使用了ref标识的对象

image.png

何时使用 Refs?

下面是几个适合使用 refs 的情况:

  • 管理焦点,文本选择或媒体播放。
  • 触发强制动画。
  • 集成第三方 DOM 库。

避免使用 refs 来做任何可以通过声明式实现来完成的事情。

举个例子,避免在 Dialog 组件里暴露 open() 和 close() 方法,最好传递 isOpen 属性。

注意

不要过度使用Refs

# 二、字符串形式的ref

定义: 直接在标签中写上ref="xxx"

使用: this.refs.xxx

// 创建组件
class Demo extends React.Component {
    render() {
        return (
            <div>
                <input ref="input1" type="text" placeholder="点击按钮提示数据" />&nbsp;
                <button onClick={this.showData}>点击提示左侧的数据</button>&nbsp;
            </div>
        )
    }

    showData = () => {
        console.log(this);  // Demo实例
        // 解构
        const { input1 } = this.refs;
        console.log('input1 ==>', input1); // DOM
        console.log('value ==>', input1.value); // 输入框的值
    }
}

// 渲染组件到页面
ReactDOM.render(<Demo />, document.getElementById('test'));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

图示 image.png

注意尽量要同名,否则会替换前一个ref

这种形式也被称为过时的API,官网也推荐我们使用回调函数或者createRef()代替。

但是吧,我们也得知道有这东西哈。官网地址请点击 (opens new window)

image.png

# 三、回调函数形式的ref

什么是回调函数?

  1. 自己定义的函数

  2. 自己没有调用

  3. 最后别人帮你调用

我们这里直接使用箭头函数即可,箭头函数本身没有this,往外找是实例对象, 把当前的ref所在的节点当作函数的实参传给了你所定义的那个属性

class Demo extends React.Component {
    render() {
        // 箭头函数没有this,往外找是Demo的实例对象, 把当前的ref所在的节点当作函数的实参传给了input1属性
        return (
            <div>
                <input ref={currentNode => this.input1 = currentNode} type="text" placeholder="点击按钮提示数据"/>&nbsp;    
                <button onClick={this.showData}>点击提示左侧的数据</button>&nbsp;
            </div>
        )
    }

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

}

ReactDOM.render(<Demo />, document.getElementById('test'));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

图示 image.png

# 四、createRef创建的ref

React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,不同节点不能使用同一个,是覆盖操作,会替换掉之前的那个ref。


class Demo extends React.Component {

    // React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,不同节点不能使用同一个,是覆盖操作,会替换掉之前的那个ref
    myRef = React.createRef();
    myRef2 = React.createRef();

    render() {
        return (
            <div>
                <input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>&nbsp;    
                <button onClick={this.showData}>点击提示左侧的数据</button>&nbsp;
                <input ref={this.myRef2} onBlur={this.blurData} type="text" placeholder="失去提示数据"/>&nbsp;    
            </div>
        )
    }

    showData = () => {
        console.log(this);
        console.log(this.myRef); // {current: input} ---- current不能改,React规定是这样
        console.log('myRef ==> ', this.myRef.current.value)
    }

    blurData = () => {
        console.log('myRef2 ==> ',this.myRef2.current.value)
    }
}

ReactDOM.render(<Demo />, document.getElementById('test'));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

图示 image.png

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

# 五、回调ref中调用次数的问题

1、内联形式的回调

在更新过程中它会被执行两次,第一次传入参数 null,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。

2、class 的绑定函数

通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,因为class类函数是已经放在了this(实例对象)上,就算就重新渲染this中已经存在了,就不会重新赋值。但是大多数情况下它是无关紧要的。

也就是说这两者区别是有的,但其实没啥大问题。

案例:当切换天气的时候看函数调用了几次?

class Demo extends React.Component {

    state = {
        isHot: true
    }

    render() {
        const { isHot } = this.state;
        return (
            <div>
                <p>今天天气很{isHot ? '炎热' : '凉爽'}</p>
                {/* 内联的回调 */}
                {/*<input ref={currentNode => {this.input1 = currentNode;console.log('currentNode ==>', currentNode);}} type="text" placeholder="点击按钮提示数据"/>&nbsp;*/}
                <input ref={this.saveInput} type="text" placeholder="点击按钮提示数据" />&nbsp;
                <button onClick={this.changeWeather}>点击改变天气</button>&nbsp;
                <button onClick={this.showData}>点击提示左侧的数据</button>&nbsp;
            </div>
        )
    }

    showData = () => {
        const { input1 } = this;
        alert(input1.value)
    }

    // 绑定class类的函数
    saveInput = (currentNode) => {
        this.input1 = currentNode;
        console.log('currentNode ==>', currentNode);
    }

    // 改变天气,存放在实例对象上的函数
    changeWeather = () => {
        const { isHot } = this.state;
        this.setState({
            isHot: !isHot
        })
    }
}

ReactDOM.render(<Demo />, document.getElementById('test'));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

图示 image.png

# 六、小总结

  1. 创建ref的三种方式:分别是字符串形式,回调函数,createRef()
  2. 命名的时候应尽量避免重复命名,否则会产生一个覆盖操作
  3. 不要过度的使用ref
在线编辑 (opens new window)
#React
上次更新: 2021/11/14, 07:48:46
React组件三大核心属性 - Props
hooks

← React组件三大核心属性 - Props hooks→

最近更新
01
毛玻璃效果
11-23
02
svg基本绘制
11-23
03
滑动登录界面
11-23
更多文章>
🖥️

© 2021 小胖墩er 💌 粤ICP备2021158933号 🛀 Theme by 💝 Vdoing

  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×