1. 首页

彻底理解 React hook useCallback和useMemo的区别

前言

最近看了React hook useCallback 和 useMemo的文档当但是看完是一知半解(我的理解能力有点差🤣),感觉这两个api非常雷同,对于使用场景一时无法想到,就静下来研究一下,特此分享下,我会按照以下目录分享

  • useCallback
    • useCallback 的参数
    • useCallback 返回值
    • useCallback 使用场景
  • useMemo
    • useMemo 的参数
    • useMemo 的返回值
    • useMemo 使用场景

useCallback

看一下(一定要看完哦,对下面理解很重要),使用class写父子组件嵌套,父组件传递一个方法给子组件的场景,下面是一个不理想的写法


class ParentComponent extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } //这是采用不理想的写法 handleChildren() { console.log('clicked ChildrenComponent'); } //这是采用不理想的写法 handleParent() { console.log('clicked ParentComponent'); this.setState(preCount => ({ count: preCount + 1 })); } render() { return ( <div> {/* 这是采用不理想的写法 */} <div onClick={() => { this.handleParent(); }} > ParentComponent{' '} </div> {/* 这是采用不理想的写法 */} <ChildrenComponent handleChildren={() => { this.handleParent(); }} /> </div> ); } } class ChildrenComponent extends React.PureComponent { render() { const { handleChildren } = this.props; console.log('ChildrenComponent rending'); return <div onClick={handleChildren}>ChildrenComponent </div>; } }

Js中文网 – 前端进阶资源教程 www.javascriptC.com,typescript 中文手册
专注分享前端知识,你想要的,在这里都能找到

有些小伙伴其实已经看到问题所在了,问题出在每次点击ParentComponent就会导致ChildrenComponent也渲染一次,虽然ChildrenComponent采用了PureComponent 优化

彻底理解 React hook useCallback和useMemo的区别

当ParentComponent组件的setate发生改变的时候 render都会重新渲染一次,ChildrenComponent的属性 handleChildren属性采用匿名函数赋值,导致每次的引用地址不一样,那么ChildrenComponent使用用PureComponent 优化是无效的



{/* 这是采用不理想的写法 */} <ChildrenComponent handleChildren={() => { this.handleParent(); }} />

改正版


class ParentComponent extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } handleChildren = () => { console.log('clicked ChildrenComponent'); }; handleParent = () => { console.log('clicked ParentComponent'); this.setState(preCount => ({ count: preCount + 1 })); }; render() { return ( <div> <div onClick={this.handleParent}>ParentComponent </div> <ChildrenComponent handleChildren={this.handleChildren} /> </div> ); } } class ChildrenComponent extends React.PureComponent { render() { const { handleChildren } = this.props; console.log('ChildrenComponent rending'); return <div onClick={handleChildren}>ChildrenComponent </div>; } }

点击ParentComponent不会导致ChildrenComponent也渲染,真正的起到了优化的作用

彻底理解 React hook useCallback和useMemo的区别

扯了这么多了,只要你能够明白上面的问题那个useCallback的作用也就明白了,话不多说看代码(hook的写法)

useCallback 的参数

需要传入两个参数

  • callback(仅仅是个函数),并把要做事情的函数放在callback函数体内执行
  • deps 要做事情的函数需要引入的外部参数或者是依赖参数

const handleChildrenCallback = useCallback(() => { handleChildren(); }, []);// 咱们不需要就不要传入

useCallback 返回值

返回一个 memoized 回调函数。在依赖参数不变的情况下,返回的回调函数是同一个引用地址


注意 每当依赖参数发生改变useCallback就会自动重新返回一个新的 memoized 函数(地址发生改变)

useCallback 使用场景

上面的优化子组件渲染次数,就是useCallback的使用场景,废话不说咱们看看使用怎么做到子组件的优化,咱们先看没有使用useCallback,子组件的渲染


注意 memo和PureComponent功能相同

const ParentComponent = () => { const [count, setCount] = useState(0); const handleChildren = () => { console.log('clicked ChildrenComponent'); }; const handleParent = () => { console.log('clicked ParentComponent'); setCount(preCount => preCount + 1); }; return ( <div> <div onClick={handleParent}>ParentComponent --count =={count} </div> <ChildrenComponent handleChildren={handleChildren} /> </div> ); }; const ChildrenComponent = memo(({ handleChildren }) => { console.log('ChildrenComponent rending'); return <div onClick={handleChildren}>ChildrenComponent </div>; });

每次点击ParentComponent就会导致ChildrenComponent也渲染一次,虽然ChildrenComponent采用了memo 优化,看图

彻底理解 React hook useCallback和useMemo的区别

使用useCallback,来优化ChildrenComponent的渲染,看代码


const ParentComponent = () => { const [count, setCount] = useState(0); const handleChildren = () => { console.log('clicked ChildrenComponent'); }; const handleChildrenCallback = useCallback(() => { handleChildren(); }, []); const handleParent = () => { console.log('clicked ParentComponent'); setCount(preCount => preCount + 1); }; return ( <div> <div onClick={handleParent}>ParentComponent --count =={count} </div> <ChildrenComponent handleChildren={handleChildrenCallback} /> </div> ); }; const ChildrenComponent = memo(({ handleChildren }) => { console.log('ChildrenComponent rending'); return <div onClick={handleChildren}>ChildrenComponent </div>; });

点击ParentComponent不会导致ChildrenComponent渲染,真正的起到了优化的作用 ,看动图

彻底理解 React hook useCallback和useMemo的区别

第二参数传入值并且去触发ChildrenComponent也渲染,代码思路是每点击三次就会触发ChildrenComponent渲染一次


const ParentComponent = () => { const [count, setCount] = useState(1); const [updateChildrenComponentNum, setUpdateChildrenComponentNum] = useState( 0 ); const handleChildren = updateChildrenComponentNum => { console.log( 'clicked ChildrenComponent updateChildrenComponentNum ' + updateChildrenComponentNum ); }; const handleChildrenCallback = useCallback(() => { handleChildren(updateChildrenComponentNum); }, [updateChildrenComponentNum]); const handleParent = () => { console.log('clicked ParentComponent'); setCount(preCount => preCount + 1); if (count % 3 === 0) setUpdateChildrenComponentNum(preNum => preNum + 1); }; return ( <div> <div onClick={handleParent}>ParentComponent --count =={count} </div> <ChildrenComponent handleChildren={handleChildrenCallback} /> </div> ); }; const ChildrenComponent = memo(({ handleChildren }) => { console.log('ChildrenComponent rending'); return <div onClick={handleChildren}>ChildrenComponent </div>; });

每点击三次就会触发ChildrenComponent渲染一次 看图

彻底理解 React hook useCallback和useMemo的区别

🤔大伙应该明白了useCallback的作用了,配合memo用于优化子组件的渲染次数

useMemo

useMemo有是的作用是什么呢?是避免在每次渲染时都进行高开销的计算的优化的策略,

useMemo 的参数

需要传入两个参数

  • callback(仅仅是个函数),并把要做事情的函数放在callback函数体内执行,(需要有返回值)
  • deps 要做事情的函数需要引入的外部参数或者是依赖参数

useMemo 的返回值

  • 返回一个 memoized 值。在依赖参数不变的的情况返回的是上次第一次计算的值

注意 每当依赖参数发生改变useMemo就会自动重新计算返回一个新的 memoized值

useMemo使用场景

  • 优化针对于当前组件高开销的计算,具有记忆功能

看代码,先演示没有使用useMemo,发现 computeExpensiveValue每次都会重现计算,遇见大的计算量是会很吃内存


const ComputeComponent = () => { const [count, setCount] = useState(100); const [changeNum, setChangeNum] = useState(100); function computeExpensiveValue(count) { console.log('computeExpensiveValue 被执行'); //比较大计算 const array = new Array(count).fill(count); return array.reduce((currentTotal, item) => { return currentTotal + item; }, 0); } const handleSetCount = () => { setCount(preCount => preCount * 2); }; const handleChangeNum = () => { setChangeNum(preCount => preCount * 2); }; const computeValue = computeExpensiveValue(count); return ( <div> <div>{computeValue}</div> <div onClick={handleSetCount}>addCount{count} </div> <div onClick={handleChangeNum}> add changeNum {changeNum}</div> </div> ); };

不论我点击 addCount 还是 add changeNum computeExpensiveValue都会被执行,看图

彻底理解 React hook useCallback和useMemo的区别

其实我们不希望在不改变count值的时候去重新执行computeExpensiveValue,使用useMemo,是可以做到,开代码


const ComputeComponent = () => { const [count, setCount] = useState(100); const [changeNum, setChangeNum] = useState(100); const computeValue = useMemo(() => computeExpensiveValue(count), [count]); function computeExpensiveValue(count) { console.log('computeExpensiveValue 被执行'); //比较大计算 const array = new Array(count).fill(count); return array.reduce((currentTotal, item) => { return currentTotal + item; }, 0); } const handleSetCount = () => { setCount(preCount => preCount * 2); }; const handleChangeNum = () => { setChangeNum(preCount => preCount * 2); }; return ( <div> <div>{computeValue}</div> <div onClick={handleSetCount}>addCount{count} </div> <div onClick={handleChangeNum}> add changeNum {changeNum}</div> </div> ); };

我们只希望在点击addCount或者是count变化的时候去重新计算,看图

彻底理解 React hook useCallback和useMemo的区别

不同之处

  • useCallback 优化针对于子组件渲染
  • useMemo 优化针对于当前组件高开销的计算

如有不足,可以留言指出😁

作者:zhangfaliang
链接:https://juejin.im/post/5e0450a7f265da33f21658fc

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文著作权归作者所有,如若转载,请注明出处

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:彻底理解 React hook useCallback和useMemo的区别

链接:https://www.javascriptc.com/3793.html

« 大流量冲击下,腾讯 QQ 客户端如何保障春节红包活动的用户体验?
写给初中级前端的高级进阶指南(万字长文,路线明确)。»
Flutter 中文教程资源

相关推荐

QR code