React进阶用法和hooks的个人使用见解(Typescript版本) - 3.useCallback+useMemo+memo性能优化

react 同时被 2 个专栏收录
11 篇文章 1 订阅
22 篇文章 0 订阅

3.useCallback+useMemo+memo性能优化场景使用

注意:hooks只能在函数(无状态组件)中使用

当父组件引入子组件的情况下,往往会造成组件之间的一些不必要的浪费,下面我们通过例子来了解一下场景

3.1创建一个test.tsx文件,我们拷贝上篇博客的useState的案例
import React, { useState } from 'react';
function Test() {
    const [count, setCount] = useState<number>(100);
    return (
        <>
            <h2>{count}</h2>
            <button onClick={() => setCount(count + 1)}>++</button>
            {/* 引入子组件 */}
            <TestChild/>
        </>
    )
}
export default Test;
//创建一个子组件
function TestChild(): JSX.Element {
    console.log('TestChild运行了?');
    return(
        <h3>我是子组件</h3>
    );
}
这时我们开启服务运行一下这个小案例,会发现,我们的子组件并没有对应的需要更新的操作但是还是触发了,这时候我们需要使用React的memo来优化一下代码

我们会发现

3.2使用memo优化react性能
import React, { useState,memo } from 'react';
//在TestChild子组件使用之前,使用memo包裹一下
const MemoTestChild = memo(TestChild);//对子组件进行处理
function Test() {
    const [count, setCount] = useState<number>(100);
    return (
        <>
            <h2>{count}</h2>
            <button onClick={() => setCount(count + 1)}>++</button>
            {/* 引入子组件 */}
            {/* <TestChild/> */}
            <MemoTestChild/>
        </>
    )
}
export default Test;
//创建一个子组件
function TestChild(): JSX.Element {
    console.log('TestChild运行了?');
    return(
        <h3>我是子组件</h3>
    );
}
这时我们看一下会不会产生上述的问题

在这里插入图片描述
从上面可以看出,除了初始化的执行之外,这时候父组件发生状态的改变,子组件不会发生对应的重新执行,优化了代码的性能,个人建议多使用这些性能优化的函数,以提高性能

3.3我们再看一个父组件传值传函数的例子(相同的例子改造)


import React, { useState, memo } from 'react';
//memo性能优化后的子组件
const MemoTestChild = memo(TestChild);
function Test(): JSX.Element {
    const [count, setCount] = useState<number>(100);
    const [name, setName] = useState<string>('TestChild组件');
    return (
        <>
            <h2>{count}</h2>
            <button onClick={() => setCount(count + 1)}>++</button>
            {/* 引入子组件 */}
            {/* <TestChild/>       把父组件的状态和设置状态的函数传递给子组件     */}   
            <MemoTestChild name={name} onClick={(newName: string) => setName(newName)} />
        </>
    )
}
export default Test;

//子组件部分
interface TestChildPropsType {
    name: string;
    onClick: Function;
}
function TestChild({ name,onClick }: TestChildPropsType): JSX.Element {
    console.log('TestChild运行了?');
    return (
        <>
            <h3>我是子组件,这是父组件传递过来的数据:{name}</h3>
            <button onClick={onClick.bind(null,'新的子组件name')}>改变name</button>
        </>
    );
}

这是传递给子组件一个新的状态,然后我们看看点击父组件后的情况

在这里插入图片描述
纳尼?怎么又给我执行了???

这时我们发现,当我们传递状态给子组件的时候,memo好像没什么效果,子组件还是执行了,这时候我们就要引入hooks的useCallback、useMemo这两个钩子了,不知道的大佬可以观看官方文档的介绍,这里就不做介绍了,只是一些实际场景的运用

官方useCallback、useMemo地址

对useCallback、useMemo简单的介绍(官方解释)
useCallback:把内联回调函数及依赖项数组作为参数传入 useCallback,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新。当你把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate)的子组件时,它将非常有用。
useMemo:把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。
两个都是返回一个memoized,同时具备第二个参数依赖项,第二个参数的情况和useEffect类似,但是useCallback往往使用于传递给子组件的函数的优化,useMemo使用于数据的优化
3.4这时候我们对上面的代码进行改造,改造成如下代码(引入useCallback,优化函数)

import React, { useState, memo, useCallback } from 'react';
//memo性能优化后的子组件
const MemoTestChild = memo(TestChild);
function Test(): JSX.Element {
    const [count, setCount] = useState<number>(100);
    const [name, setName] = useState<string>('TestChild组件');
    return (
        <>
            <h2>{count}</h2>
            <button onClick={() => setCount(count + 1)}>++</button>
            {/* 引入子组件 */}
            {/* <TestChild/>       把父组件的状态和设置状态的函数传递给子组件     */}
            <MemoTestChild name={name} onClick={useCallback((newName: string) => setName(newName),[])} />
            {/* useCallback((newName: string) => setName(newName),[]) */}
            {/* 这里使用了useCallback优化了传递给子组件的函数,只初始化一次这个函数,下次不产生新的函数 */}
        </>
    )
}
export default Test;

//子组件部分
interface TestChildPropsType {
    name: string;
    onClick: Function;
}
function TestChild({ name, onClick }: TestChildPropsType): JSX.Element {
    console.log('TestChild运行了?');
    return (
        <>
            <h3>我是子组件,这是父组件传递过来的数据:{name}</h3>
            <button onClick={onClick.bind(null, '新的子组件name')}>改变name</button>
        </>
    );
}
这时候我们看见子组件不会在父组件与子组件无关状态改变的时候执行,不会一直产生重新产生新函数,useCallback第二个参数,是依赖项,可以确定在什么状态改变的情况下产生一个新的回调函数

在这里插入图片描述

这时候我们会想到,useMemo要使用在什么场景之下呢?
我们继续改造一下这个案例,把name改成一个对象

import React, { useState, memo, useCallback } from 'react';
//memo性能优化后的子组件
const MemoTestChild = memo(TestChild);
function Test(): JSX.Element {
    const [count, setCount] = useState<number>(100);
    const [name, setName] = useState<string>('TestChild组件');
    return (
        <>
            <h2>{count}</h2>
            <button onClick={() => setCount(count + 1)}>++</button>
            {/* 引入子组件 */}
            {/* <TestChild/>       把父组件的状态和设置状态的函数传递给子组件     */}
            <MemoTestChild
                name={{ name, color: name.indexOf('name') !== -1 ? 'red' : 'green' }}
                onClick={useCallback((newName: string) => setName(newName), [])}
            />
            {/* useCallback((newName: string) => setName(newName),[]) */}
            {/* 这里使用了useCallback优化了传递给子组件的函数,只初始化一次这个函数,下次不产生新的函数 */}
        </>
    )
}
export default Test;

//子组件部分
interface TestChildPropsType {
    name: { name: string; color: string };
    onClick: Function;
}
function TestChild({ name, onClick }: TestChildPropsType): JSX.Element {
    console.log('TestChild运行了?');
    return (
        <>
            <h3 style={{ color: name.color }}>我是子组件,这是父组件传递过来的数据:{name.name}</h3>
            <button onClick={onClick.bind(null, '新的子组件name')}>改变name</button>
        </>
    );
}
这时候我们会发现,子组件还是一样的执行了,在父组件更新其它状态的情况下,子组件的name对象属性会一直发生重新渲染改变,从而导致一直执行,这也是不必要的性能浪费

在这里插入图片描述

3.5这时候我们就需要使用useMemo这个钩子来优化传递的子属性了

import React, { useState, memo, useCallback, useMemo } from 'react';
//memo性能优化后的子组件
const MemoTestChild = memo(TestChild);
function Test(): JSX.Element {
    const [count, setCount] = useState<number>(100);
    const [name, setName] = useState<string>('TestChild组件');
    return (
        <>
            <h2>{count}</h2>
            <button onClick={() => setCount(count + 1)}>++</button>
            {/* 引入子组件 */}
            {/* <TestChild/>       把父组件的状态和设置状态的函数传递给子组件     */}
            <MemoTestChild
                // 使用useMemo,返回一个和原本一样的对象,第二个参数是依赖性,当name发生改变的时候,才产生一个新的对象
                name={useMemo(() => ({ name, color: name.indexOf('name') !== -1 ? 'red' : 'green' }),[name])}
                onClick={useCallback((newName: string) => setName(newName), [])}
            />
            {/* useCallback((newName: string) => setName(newName),[]) */}
            {/* 这里使用了useCallback优化了传递给子组件的函数,只初始化一次这个函数,下次不产生新的函数 */}
        </>
    )
}
export default Test;

//子组件部分
interface TestChildPropsType {
    name: { name: string; color: string };
    onClick: Function;
}
function TestChild({ name, onClick }: TestChildPropsType): JSX.Element {
    console.log('TestChild运行了?');
    return (
        <>
            <h3 style={{ color: name.color }}>我是子组件,这是父组件传递过来的数据:{name.name}</h3>
            <button onClick={onClick.bind(null, '新的子组件name')}>改变name</button>
        </>
    );
}

在这里插入图片描述

这样我们的一个性能优化小例子就都完成了

最后总结:在子组件不需要父组件的值和函数的情况下,只需要使用memo函数包裹子组件即可。而在使用值和函数的情况,需要考虑有没有函数传递给子组件使用useCallback,值有没有所依赖的依赖项而使用useMemo,而不是盲目使用这些hooks等

第四节我们来讨论useReducer+useContext+createContext的使用、模仿redux的combineReducers函数合并reducer案例

需要了解其它React进阶用法和hooks

React进阶用法和hooks的个人使用见解:
1.lazy+Suspense懒加载的使用

2.hooks的useState、useEffect、自定义钩子的实际使用

4.useReducer+useContext+createContext的使用、模拟redux合并reducer

5.useRef,useImperativeHandle和forwardRef的结合使用以及useLayoutEffect、useDebugValue的简单使用

  • 10
    点赞
  • 2
    评论
  • 14
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值