IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> react hooks系列_useCallback -> 正文阅读

[JavaScript知识库]react hooks系列_useCallback

? ? ? ? ?这篇文章有点晦涩难懂,并且我做了很多的铺垫,就是希望,大家能够用这一篇文章彻底搞懂 useCallBack。所以,希望大家能够静下心来,仔细品,同时,把代码也执行一下。否则,根本看不懂,或者可以多看几遍。

一、抛出问题

以前写类组件时,经常会写以下(有隐含性能的)代码

export default class Parent extends React.Component {   
    
    tempfn(v){
        
    }
    render = () => (
        <div>
             <Son person={{name:"张三疯"}} onMyClick={(val)=>this.tempFn(val)} />
        </div>
    )
}

以上代码存在的问题,当父组件(Parent)重新渲染引起子组件Son的渲染。那么就会重新定义对象:{name:张三疯} ,也还会重新定义函数(val)=>this.tempFn(val)。diff算法在比较虚拟dom时,发现有变化,就会渲染的更多。耗费cpu性能和内存。如果说这两个对象在渲染前后,没有变化,那么,就会节约性能。

解决办法:把它们抽取成变量。如下:

export default class Parent extends React.Component {   
    constructor(props){
        super(props);
        this.state={
            person:{name:"张三疯"}
        }
        this.tempFn = this.tempFn.bind(this);
    }
    tempFn(val){
        
    }
    render = () => (
        <div>
             <Son person={this.state.person} onMyClick={this.tempFn} />
        </div>
    )
}

但是,在函数式组件里,就没有办法了,因为,类组件里可以用this存储这个函数,而在函数式组件了,没法存储这个函数。因为,函数式组件,每次重新渲染时,会把函数里的代码全部执行,而类组件只执行render函数里的代码(你可以认为函数式组件就是类组件中的render方法) ??难道对象和函数不能定义在函数式组件的外面吗??,答案是不行(你可以试试)。

二、解决问题:

1、初步认识useCallBack

useCallBack能干什么?

1)、useCallback会返回一个函数的memoized(记忆的)值

2)、在依赖不变的情况下,多次定义(如:函数)的时候,返回的值是相同的

格式:

let 新的函数 = useCallback(曾经的函数, [依赖的值]) 

示例代码:

import {useState,useCallback} from "react"

export default ()=>{
    
    //1、 定义一个状态(基本类型):count
    const [count,setCount] = useState(0);

    let changecount = useCallback(() => {
        setCount(count + 1)
      }, [count]) //依赖了count,所以,只要count有变化,就会执行 setCount(count + 1)
 
    let changecount = useCallback(() => {
        setCount(count + 1)
    }, [])//这种写法,没有依赖值,那么不会执行 setCount(count + 1)

    return (
        <div>
            <input type="button" value="修改count2" onClick={changecount} />
            <p>count:{count}</p>
        </div>
    )
}

2、那么,useCallBack到底在何处提高了性能?

答: 通常在将一个组件中的函数,传递给子元素进行回调使用时,使用useCallback对函数进行处理

示例代码:

1)、首先需要知道,父组件更新时,子组件也会更新

const MySon = (props)=>{
    console.log("props.name",props.name)
    return  <input type="button" value={props.name} />
}

export default function Myparent() {
  
    const [p,setP] = useState(0)
    
    console.log("父组件更新了");
    return (
      <div>
        <MySon name="btn1" />
        <input type="button" value="加一" onClick={e=>setP(p+1)} />
      </div>
    )
  }

2)、如果父组件更新时,不希望子组件更新,使用memo( React.memo()是一个高阶函数,它与 React.PureComponent类似, React.memo()用在函数式组件里,防止无效的更新)

import {useState,memo} from "react"

const MySon = memo((props)=>{
    console.log("props.name",props.name)
    return  <input type="button" value={props.name} />
})

3)、当然,如果出现了,父组件修改的数据影响了子组件的数据(props),那么子组件肯定是要更新的

const MySon = memo((props)=>{
    console.log("props.name",props.name)
    return <input type="button" value={props.name} />
})

export default function Myparent() {
  
    const [p,setP] = useState(0);
    const [name,setName]=useState("hi");
    
    console.log("父组件更新了");
    return (
      <div>
        <MySon name={name}  />
         {/*点击这个按钮,子组件不更新*,因为,p的值并没有在子组件中使用/}
        <input type="button" value="加一" onClick={e=>setP(p+1)} /> 
         {/*点击这个按钮,子组件会更新,因为,name是传入子组件的值*/}
        <input type="button" value="修改Name" onClick={e=>setName("haha")} />
      </div>
    )
  }  

4)、但是:子组件使用父组件的函数(父组件给子组件传递函数)时,虽然,函数体没有变化,但是父组件更新依然会更新。这不是我们希望的。

解释:在父组件里定义的函数,每次的值(函数体)是不变了的。由于,父组件更新时,重新定义函数(new Function()),那么函数的值(地址)会发生变化。所以,引起子组件的更新了

const MySon = memo((props)=>{
    console.log("props.name",props.name)
    return <input type="button" value={props.name} onClick={props.increament} />
})

export default function Myparent() {
  
    const [p,setP] = useState(0);
    const [name,setName]=useState("hi");

    const  increament1 = () => {
        console.log('执行increament1')
    }
        
    console.log("父组件更新了");
    return (
      <div>
        <MySon name={name} increament={increament1} />
        {/*点击下面按钮时,看上去并没有修改子组件所使用的的数据name和increment。
          但是,实际上,increment1的值发生变化了。
          因为:
             1、父组件更新时,重新定义了函数(即:重新new Function()),即:变量increment1的值(存储的地址)发生变化了
             2、对于memo来说,只做了了浅比较,只要变量的值(对于对象来说,在内存中存储的就是地址)发生了变化,那么,就会引起组件的更新。
        */}
        <input type="button" value="加一" onClick={e=>setP(p+1)} />
      </div>    
    )
  }

5)、useCallBack出场了

首先,假定 increament1函数里依赖着某个数据(如:count),即:当count发生变化时,increament1函数才会重新定义。

其次,把increment1函数用useCallBack进行包裹。这样就不会出现无效的刷新(只要count的值不变,函数increament1就不会重新定义,子组件就不会重新刷新)

const  increament1 = useCallback(() => {
        console.log('执行increament1')
    },[count])

最终的示例代码:

import {useState,useCallback,memo} from "react"

const MySon = memo((props)=>{
    console.log("props.name",props.name)
    return <input type="button" value={props.name} onClick={props.increament} />
})

export default function Myparent() {
  
    const [p,setP] = useState(0);
    const [name,setName]=useState("hi");
    const [count,setCount] = useState(0);

    //increment1函数是否要重新定义,取决于count的值是否发生变化
    const  increament1 = useCallback(() => {
        console.log('执行increament1')
    },[count])
        
    console.log("父组件更新了");
    return (
      <div>
        <MySon name={name} increament={increament1} />
        {/*点击下面这个按钮,子组件不会刷新,因为,并没有修改count的值*/}
        <input type="button" value="p加一" onClick={e=>setP(p+1)} />
        {/*点击下面这个按钮,子组件会刷新,因为,increment1函数是否要重新定义,取决于count的值是否发生变化,下面这个按钮修改了count的值*/}
        <input type="button" value="count加1" onClick={e=>setCount(count+1)} />
      </div>    
    )
  }

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-07-05 20:19:21  更:2021-07-05 20:19:27 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年4日历 -2024/4/24 9:13:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码