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中memo,useCallback和useMemo的基础介绍和使用 -> 正文阅读

[JavaScript知识库]React学习笔记——Hooks中memo,useCallback和useMemo的基础介绍和使用

在 React 的世界中,有容器组件和 UI 组件之分,在 React Hooks 出现之前,UI 组件我们可以使用函数,无状态组件来展示 UI,而对于容器组件,函数组件就显得无能为力,我们依赖于类组件来获取数据,处理数据,并向下传递参数给 UI 组件进行渲染。使用 React Hooks 相比于从前的类组件有以下几点好处:

  1. 代码可读性更强,原本同一块功能的代码逻辑被拆分在了不同的生命周期函数中,容易使开发者不利于维护和迭代,通过 React Hooks 可以将功能代码聚合,方便阅读维护
  2. 组件树层级变浅,在原本的代码中,我们经常使用 HOC/render props 等方式来复用组件的状态,增强功能等,无疑增加了组件树层数及渲染,而在 React Hooks 中,这些功能都可以通过强大的自定义的 Hooks 来实现

Hook 是 React 16.8.0 版本增加的新特性,可以在函数组件中使用 state以及其他的 React 特性
Hooks只能在函数式组件中使用,既无状态组件(所有钩子在用时都要先引入)

1、Hook 使用规则

Hook 就是JavaScript 函数,但是使用它们会有两个额外的规则:
1、只能在函数最外层调用 Hook。不要在循环、条件判断或者嵌套函数(子函数)中调用。
2、只能在 React 的函数组件中调用 Hook。不要在其他 JavaScript 函数中调用。
3、在多个useState()调用中,渲染之间的调用顺序必须相同

2、memo

当子组件是类组件的时候可以使用shouldComponentUpdate钩子函数或类组件继承PureComponent实现不渲染子组件。但是对于函数组件来说是不能用这两个方法的,因此react官方给函数组件提供了memo来对函数组件包装下,实现不必要的渲染。

(1)官网描述

React.memo 为高阶组件

如果你的组件在相同 props 的情况下渲染相同的结果,那么你可以通过将其包装在 React.memo 中调用,以此通过记忆组件渲染结果的方式来提高组件的性能表现
这意味着在这种情况下,React 将跳过渲染组件的操作并直接复用最近一次渲染的结果。

React.memo 仅检查 props 变更。如果函数组件被 React.memo 包裹,且其实现中拥有 useState,useReduceruseContext的 Hook,当 context 发生变化时,它仍会重新渲染

(2)实例说明
import React , {useState} from 'react';
const Parent = () => {
    const [count, setCount] = useState(0);
    return (
        <div>
        父组件的count---------{count}
        <button onClick={()=>setCount(count + 1)}>点击按钮</button>
        <Child />
      </div>
    )
};
const Child = () => {
    console.log('子组件-----render');
    return (
      <div>
        子组件
      </div>
    )
}
export default Parent;

在这里插入图片描述
这里我们父组件内部改变count,但是count值并没有传递给子组件,但是子组件一样的重新渲染了,这并不是我们希望看到的。

所以我们使用memo对子组件包装。

import React , {useState , memo} from 'react';

const Parent = () => {
    const [count, setCount] = useState(0);
    return (
        <div>
        父组件的count---------{count}
        <button onClick={()=>setCount(count + 1)}>点击按钮</button>
        <Child />
      </div>
    )
};
const Child = memo(() => {
    console.log('子组件-----render');
    return (
      <div>
        子组件
      </div>
    )
})

export default Parent;

在这里插入图片描述
使用memo进行包装之后,我们可以发现,子组件没有随着父组件的count改变而多次渲染。

(3)注意点

父组件传递状态给子组件的时候,即使子组件并没有使用父组件传递过来的状态,这种情况下依然会渲染子组件,memo好像没什么效果

import React , {useState , memo} from 'react';

const Parent = () => {
    const [count, setCount] = useState(0);
    return (
        <div>
        父组件的count---------{count}
        <button onClick={()=>setCount(count + 1)}>点击按钮</button>
        {/* 这种,子组件会重新渲染, */}
        <Child aaa={count}/>
      </div>
    )
};
const Child = memo(() => {
    console.log('子组件-----render');
    return (
      <div>
        子组件
      </div>
    )
})

export default Parent;

在这里插入图片描述
所以我们需要配合使用useCallbackuseMemo来帮助我们进行优化。

3、useMemo和useCallback的区别

  • useMemo和useCallback都是具有缓存作用的,只是他们缓存对象不一样,一个是属性,一个是缓存函数。但是,特点都是:当缓存依赖的没变,去获取还是获取曾经的缓存
  • useMemo对函数组件中的属性包装返回一个具有缓存效果的新的属性,当依赖的属性没变化的时候,这个返回新属性就会从缓存中获取之前的
  • useCallback对函数组件中的方法缓存返回一个被缓存的方法

官网上有这么一句话来描述两者关系:

useCallback(fn, deps) 相当于 useMemo(() => fn, deps)。

4、useMemo

(1)官网描述
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
  • 返回一个 memoized 值
  • 把“创建”函数依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值这种优化有助于避免在每次渲染时都进行高开销的计算
  • 记住,传入 useMemo 的函数会在渲染期间执行。请不要在这个函数内部执行与渲染无关的操作,诸如副作用这类的操作属于 useEffect 的适用范畴,而不是 useMemo。
  • 如果没有提供依赖项数组,useMemo 在每次渲染时都会计算新的值
(2)实例说明
  1. 类似上面的方式我们在父组件更新数据,观察子组件变化(不使用useMemo)
import React , {useState,memo} from 'react';

const Parent = () => {
    const [count, setCount] = useState(0);
    const [number, setNumber]=useState(0)
    const userInfo = {
        age: count,
        name: 'hello',
    }
    const btnHandler = () => {
        setNumber(number => number+1);
    }
    return (
        console.log('父组件-----render'),
        <div>
            number:{number}-----count:{count}
            <button onClick={btnHandler}>按钮</button>
            <Child userInfo={userInfo}></Child>
        </div>
    );
};

const Child = memo(() => {
    return (
        console.log('子组件-----render'),
        <div>
            子组件
        </div>
    )
})
export default Parent;

在这里插入图片描述
我们发现,仅仅是在父组件更新了number的值,传递给子组件的userInfo对象值并没有变化,但是每次子组件都重新更新了,虽然我们在子组件上用了React.memo包装还是不行。这是因为在父组件中每次重新渲染,对于对象来说会是重新一个新的对象了。因此子组件要重新更新

  1. 使用useMemo对属性的包装
const userInfo = useMemo(() => {
	 return {
       age: count,
       name: 'hello',
 	};
}, [count]);

使用useMemo包装后的对象,重新返回一个具有缓存效果的新对象

第二个参数表示依赖性,或者叫观察对象,当被观察的没变化,返回的就是缓存对象;如果被观察的变化了,那么就会返回新的,现在不管你怎么更新number的值,子组件都不会重新更新了。

这时再看效果图
在这里插入图片描述

(3)注意点

useMemo要配合React.memo来使用,不然传递到子组件也是不生效的。

5、useCallback

(1)官网描述
const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);
  • 返回一个 memoized 回调函数。
  • 内联回调函数依赖项数组作为参数传入 useCallback,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新
(2)实例说明
  1. 类似上面的方式我们在父组件更新数据,观察子组件变化(不使用useCallback)
import React , {useState,useRef,memo} from 'react';

const Parent = () => {
   const [text, updateText] = useState('');
   const textRef = useRef(text);
   const handleSubmit = () => {
       console.log('当前的值', text);
   }

   return(
       console.log('父组件----render'),
       <div>
           我是父组件
           <input type="text" value={text} onChange={(e) => updateText(e.target.value)}/>
           <Child onAdd={handleSubmit}/>
       </div>
   )
   
};

const Child = memo((props) => {
   return (
       console.log('子组件----render'),
       <button onClick={props.onAdd}>点击按钮获取值</button>
   )
})

export default Parent;

在这里插入图片描述
我们发现,仅仅是在父组件输入的时候更新了text值,但是结果是每次输入框输入值的时候,子组件就会重新渲染一次。其实子组件中仅仅是一个按钮,要获取最终输入的值,每次父组件输入值的时候,子组件就更新,很耗性能的。

  1. 使用useCallback来包装一个方法
import React , {useState,useRef,memo,useCallback,useEffect} from 'react';

const Parent = () => {
    const [text, updateText] = useState('');
    const textRef = useRef(text);

    // useCallback又依赖了textRef的变化,因此可以获取到最新的数据
    const handleSubmit = useCallback(() => {
        console.log('当前输入框的值:', textRef.current);
    }, [textRef])

    // 当text的值变化的时候就会给textRef的current重新赋值
    useEffect(() => {
        textRef.current = text;
    }, [text]);

    return(
        console.log('父组件----render'),
        <div>
            我是父组件
            <input type="text" value={text} onChange={(e) => updateText(e.target.value)}/>
            <Child onAdd={handleSubmit}/>
        </div>
    )
    
};

效果图
在这里插入图片描述

(3)注意点

useCallback 最好 要配合React.memo来使用,不然会起到反向优化的效果。

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-07-25 11:33:33  更:2021-07-25 11:34:08 
 
开发: 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年5日历 -2024/5/2 17:18:10-

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