首页与我联系

「React Hooks 学习笔记」关于 useMemo 和 useCallback 的使用介绍(四)

By 前端达人
Published in 4-React
October 01, 2022
1 min read
「React Hooks 学习笔记」关于 useMemo 和 useCallback  的使用介绍(四)

一、开篇

大家好,在上一篇文章里《「React Hooks 学习笔记」关于 React.memo 介绍(三)》我们学习了使用 React.memo 进行缓存组件,减少组件的渲染次数,本篇文章我们继续学习 useMemouseCallback 这两个函数,这两个函数也是起到缓存的作用,最大的区别是 useMemo 缓存的是值,useCallback 缓存的是函数。

二、需求场景

接下来我们通过实例来分析下需求场景,接着上一篇文章里《「React Hooks 学习笔记」关于 React.memo 介绍(三)》的例子,我们在每个产品图片下方添加个添加到购物车的按钮,点击添加按钮,购物车的数量 + 1,具体的页面效果下图。

addtocart.png

这里我们需要用到 useCallback接下来我们完成此部分功能

2.1 使用 useCallback

首先我们来修改 SingleProduct 单个产品展示组件,将 addToCart 事件属性及按钮添加至组件,修改后的代码如下所示:

const SingleProduct = ({fields,addToCart}) => {
    useEffect(() => {
        console.log('单个产品图加载');
    })

    let {name, price} = fields
    price = price / 100
    const image = fields.image[0].url

    return (
        <article className='product'>
            <img src={image} alt={name}/>
            <h4>{name}</h4>
            <p>${price}</p>
            <button onClick={addToCart}>addToCart</button>
        </article>
    )

接下来我们需要修改 BigList 产品列表组件,将 addToCart 属性传递至子组件 SingleProduct 单个产品展示组件,示例代码如下:

const BigList = React.memo(({products,addToCart}) => {
    useEffect(() => {
        console.log('产品列表开始加载');
    })
    return (
        <section className='products'>
            {products.map((product) => {
                return <SingleProduct
                    key={product.id}
                    {...product}
                    addToCart = {addToCart}
                ></SingleProduct>
            })}
        </section>
    );
});

接下来我们在页面里修改产品列表及添加 cart 这个数据状态,并在页面显示购物车的数量,示例代码如下:

const Index = () => {
  //...省略多行代码
    const [cart, setCart] = useState(0);
   
    return (
        <>
       //...省略多行代码
            <h1 style={{ marginTop:'3rem' }}> cart: {cart}  </h1>
            <BigList products={products} addToCart={addToCart}/>
        </>
    )
}

你可能注意上述代码,列表组件里定义了 addToCart 方法,如果我们定义一个普通的方法,示例代码如下:

const addToCart =() => {
        console.log("购物车被点击了"+cart)
        setCart(cart+1);
    }

到这里,功能虽然是完成了,我们来看看实际效果,虽然点击购物车的按钮,购物车的数量是 + 1了,但是最上方的计数器按钮(click me),每点一次,你会发现 `React.memo` 这个组件起不到缓存的作用了,这是因为第二个属性 addToCart 是一个函数,数据状态发生变化时,都会创建一个新函数,造成列表组件及其子组件的重新渲染,效果如下:

addcardclick.gif

为了解决这个问题,我们需要将函数缓存起来,这时我们就需要用到 useCallback 了,我们来改造下 addToCart 函数,示例代码如下:

const addToCart =useCallback(() => {
        console.log("购物车被点击了"+cart)
        setCart(cart+1);
    },[cart])

重新运行下项目,你在点击计数器按钮,BigList 产品列表组件和 SingleProduct 单个产品展示组件将不会重新渲染。

这时我们来观察下 useCallback 的第二个参数,这里是 cart ,类似 useEffect 的第二个参数,如果 cart 内容不变,直接从缓存里读取函数,否则重新运行新函数。

你还可以尝试下去掉 cart ,猜猜控制台会如何输出。示例代码如下

const addToCart =useCallback(() => {
        console.log("购物车被点击了"+cart)
        setCart(cart+1);
    },[])

你会发现此函数只缓存了最初的函数副本,cart的初始值始终是0,每次点击添加购物车按钮,始终输出 购物车被点击了0 。

2.2 使用 useMemo

接下来我们来聊一聊如何使用 useMemo ,我们现在有个需求,需要在界面上显示当前产品中最高的单价,我们可以使用数组的 reduce 函数,我们先定义 calculateMostExpensive 函数,示例代码如下:

const calculateMostExpensive = (data) =>{
    console.log("开始计算最高单的品的价格");
    return(
        data.reduce((total,item) => {
            const price = item.fields.price
            if(price>=total){
                total = price
            }
            return total
        },0)/100
    )
}

然后在 Index 函数里调用函数,如下所示:

<h1>Most Expensive : ${calculateMostExpensive(products)}</h1>

接下来我们运行项目,你会发现,每当我们点击计数器按钮时(click me 按钮),都会重新运行我们的 calculateMostExpensive 函数,效果如下所示:

reCounter.gif

虽然界面感官上没啥变化,但是每次操作计数器,都会有每次都运行控制台都会产生“开始计算最高单的品的价格”的内容输出。 数据量小还行,数据量大真是一笔不小的系统开销(迭代寻找最大数)。由于我们的产品数据源没有发生变化,实在没有必要,这时就是使用 useMemo 的时机了。如下所示,我们在 Index 函数里定义 mostExpensive 函数:

const mostExpensive = useMemo(
        () => calculateMostExpensive(products), 
        [products]);

你会看到有两个参数,第二参数是我们需要依赖的值,和 useCallbackuseEffect 一样,这个参数发生变化时,重新运行。

2.3 接口请求方法 useFetch 的应用

在上一篇文章,就有关于 useFetch 的示例,没有过多的介绍,这是自定义 HOOKS 的运用(稍后的文章将会介绍),在这里我们使用了 useCallbackuseEffect 。结合本篇文章的介绍,我们再来熟悉下加深下理解,示例代码如下:

import{useState,useEffect,useCallback}from'react';

export constuseFetch= (url) => {
const[loading, setLoading] =useState(true);
const[products, setProducts] =useState([]);

constgetProducts =useCallback(async() => {
constresponse =await fetch(url);
constproducts =await response.json();
    setProducts(products);
    setLoading(false);
  }, [url]);

useEffect(() => {
    getProducts();
  }, [url, getProducts]);
return{ loading, products };
};

从上述代码我们看出,使用 useCallback 将接口请求函数缓存,如果 URL 发生变化,再重新触发请求。

三、总结

到这里关于 useMemouseCallback 的介绍就完了,大家是否有了清晰的认识了呢?最后我们来做个总结加深下印象吧。

共同点:

在依赖数据发生变化的时候,才会调用传进去的回调函数去重新计算结果,起到一个缓存的作用

不同点:

useMemo  缓存的结果是回调函数中 return 回来的值,主要用于缓存计算结果的值,应用场景如需要计算的相关状态值。

useCallback 缓存的是函数,应用场景如需要缓存的函数,因为函数组件每次任何一个state发生变化,会触发整个组件更新,一些没必要更新的函数,此时就应该缓存起来,提高性能,减少系统资源的开销。

前端达人公众号.jpg

注:本文属于原创文章,版权属于「前端达人」公众号及 qianduandaren.com 所有,未经授权,谢绝一切形式的转载


Tags

reacthook
Previous Article
「React Hooks 学习笔记」关于 React.memo 介绍(三)
前端达人

前端达人

专注前端知识分享

Table Of Contents

1
一、开篇
2
二、需求场景
3
三、总结

相关文章

「React Hooks 学习笔记」关于Custom Hooks 的使用介绍(八)
October 28, 2022
1 min

前端站点

VUE官网React官网TypeScript官网

公众号:前端达人

前端达人公众号