何时用 useCallback 和 useMemo

Snipaste_20200526_165641.jpg

先了解下Js数据类型方面的基础知识

          
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
true === true // true false === false // true 1 === 1 // true 'a' === 'a' // true {} === {} // false [] === [] // false () => {} === () => {} // false const z = {} z === z // true // NOTE: React 用的是 Object.is, 和 ===非常相似

先来看看下面例子

          
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
function ParentComp() { const [count, setCount] = useState(0) const increment = () => setCount(count + 1) return ( <div> <button onClick={increment}>点击次数:{count}</button> <SonComp /> </div> ); } function SonComp() { console.log('render SonComp') return <div>Son Comp</div> } export default ParentComp

这里显然父组件更新会导致子组件渲染

用上Mamo()

          
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
import React, { useState, memo } from 'react'; function ParentComp() { const [count, setCount] = useState(0) const increment = () => setCount(count + 1) return ( <div> <button onClick={increment}>点击次数:{count}</button> <MemoSonComp /> </div> ); } const MemoSonComp = memo(function SonComp() { console.log('render SonComp') return <div>Son Comp</div> })

此时子组件就不在被渲染

上面的例子并不能说明什么,因为SonComp是个纯组件。

接下来再来看看下面列子,我们给SonComp传个函数和一般的属性

          
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
import React, { useState, memo } from 'react'; function ParentComp() { const [count, setCount] = useState(0) const [name, setName] = useState('bob') const increment = () => setCount(count + 1) const changeName = (newName) => setName(newName) // 父组件渲染时会创建一个新的函数 相当于props变化了 return ( <div> <button onClick={increment}>点击次数:{count}</button> <MemoSonComp name={name} onClick={changeName} /> </div> ); } const MemoSonComp = memo(function SonComp({ name, onClick }) { console.log('render SonComp') return <> <div>Son Comp ... {name}</div> <button onClick={() => onClick('allen')}>改变 name 值</button> </> }) export default ParentComp

子组件又被渲染了

原因:父组件渲染时会创建一个新的函数 相当于props变化了,导致子组件渲染

useCallback()

这时useCallback就派上用场了

          
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
import React, { useState, memo,useCallback } from 'react'; function ParentComp() { const [count, setCount] = useState(0) const [name, setName] = useState('bob') const increment = () => setCount(count + 1) const changeName = useCallback((newName) => setName(newName), []) //包一层useCallback return ( <div> <button onClick={increment}>点击次数:{count}</button> <MemoSonComp name={name} onClick={changeName} /> </div> ); } const MemoSonComp = memo(function SonComp({ name, onClick }) { console.log('render SonComp') return <> <div>Son Comp ... {name}</div> <button onClick={() => onClick('allen')}>改变 name 值</button> </> }) export default ParentComp

此时点击父组件按钮,控制台不会打印子组件被渲染的信息了。

原因:useCallback() 起到了缓存的作用,即便父组件渲染了,useCallback() 包裹的函数也不会重新生成,会返回上一次的函数引用。

useMemo()

再来看useMemo用途

          
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
import React, { useState, memo } from 'react'; function ParentComp() { const [count, setCount] = useState(0) const [name, setName] = useState('bob') const [age, setAge] = useState(18) const increment = () => setCount(count + 1) const info = { name, age } //此处对象被新建,相当于props变化了 return ( <div> <button onClick={increment}>点击次数:{count}</button> <MemoSonComp info={info} /> </div> ); } const MemoSonComp = memo(function SonComp({ info }) { console.log('render SonComp') return <> <div>Son Comp ... {info.name}</div> </> }) export default ParentComp

同样此处对象被新建,相当于props变化了,子组件被渲染

用上useMemo()

          
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
import React, { useState, memo,useMemo } from 'react'; function ParentComp() { const [count, setCount] = useState(0) const [name, setName] = useState('bob') const [age, setAge] = useState(18) const increment = () => setCount(count + 1) const info=useMemo(() => ({ name, age }), [name, age]) //此处对象被新建,相当于props变化了 return ( <div> <button onClick={increment}>点击次数:{count}</button> <MemoSonComp info={info} /> </div> ); } const MemoSonComp = memo(function SonComp({ info }) { console.log('render SonComp') return <> <div>Son Comp ... {info.name}</div> </> }) export default ParentComp

子组件即可不被渲染

本文代码参考: https://www.jianshu.com/p/014ee0ebe959

——THE END——
(完)
开始弃用Prettier
Prettier的缺点及eslint的快速配置
何时用 useCallback 和 useMemo
了解即可,做优化时再用
解读Promise
Promise,宏任务和微任务相关
React生命周期
一张图,一段代码
2021年终总结
2022元旦快乐
随便谈谈最近股市
全网都是AI了吧
等待你的评论