背景
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import React, {Fragment} from 'react' import { useState, useMemo } from 'react'
const nameList = ['apple', 'peer', 'banana', 'lemon']
const example = (props) => { const [price, setPrice] = useState(0) const [name, setName] = useState('apple') function getProductName() { console.log('getProductName触发') return name }
return ( <Fragment> <p>{name}</p> <p>{price}</p> <p>{getProductName()}</p> <button onClick={() => setPrice(price+1)}>价钱+1</button> <button onClick={() => setName(nameList[Math.random() * nameList.length << 0])}>修改名字</button> </Fragment> ) } export default example
|
现在问几个问题:
发生下面几种情况会重新渲染界面吗(也就是getProductName
函数会被触发)?
- 点击价钱+1按钮?
- 点击修改名字按钮?
很显然在进行DOM
相关操作(如setState
)后,都会触发getProductName
函数,但是我们想知道这个产品的名字,产品的价格怎么变不是我们关心的,所以我们需要让这个函数只在产品名字改变的时候再触发,而不是每次重新渲染都触发。
useEffect ?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| import React, {Fragment} from 'react' import { useState, useEffect, useCallback, useMemo } from 'react' import { observer } from 'mobx-react'
const nameList = ['apple', 'peer', 'banana', 'lemon'] const Example = observer((props) => { const [price, setPrice] = useState(0) const [name, setName] = useState('apple') function getProductName() { console.log('getProductName触发') return name } useEffect(() => { console.log('name effect 触发') getProductName() }, [name]) useEffect(() => { console.log('price effect 触发') }, [price])
return ( <Fragment> <p>{name}</p> <p>{price}</p> <p>{getProductName()}</p> <button onClick={() => setPrice(price+1)}>价钱+1</button> <button onClick={() => setName(nameList[Math.random() * nameList.length << 0])}>修改名字</button> </Fragment> ) }) export default Example
|
- 先看看
useEffect
的工作顺序,若点击修改名字按钮会打印什么?
1 2 3
| > getProductName触发 > name effect 触发 > getProductName触发
|
官方文档有说过 当你调用 useEffect 时,就是在告诉 React 在完成对 DOM 的更改后运行你的“副作用”函数
所以这个顺序很好理解
- 因为修改了名字,然后
react
更改了DOM
,触发了getProductName
- 随后调用了
name
的effect
(在dom
更新之后触发,这也是为什么叫做副作用)
effect
中调用了getProductName
若点击价钱+1按钮会打印什么?
1 2
| > getProductName触发 > price effect 触发
|
我改变的是价格,还是触发了getProductName
稍微分析:
- 显然当我使用
setPrice
的时候,产生DOM
操作,刷新页面DOM
的同时也,触发了在p
标签中的getProductName
函数
- 然后调用副作用触发了
price
的effect
就如前面我所提出的问题,我们的目标是在DOM
发生变化时,不相关的函数不需要触发(也就是这里的getProductName
在我修改价格的时候不应该触发),而useEffect
只能在DOM
更新后再触发再去控制,所以这个马后炮并没有什么🐦用
useMemo?
使用useMemo
可以解决这个问题
为什么useMemo
可以解决?官方文档说过传入 useMemo 的函数会在渲染期间执行,所以使用useMemo
就能解决之前的问题,怎么在DOM
改变的时候,控制某些函数不被触发。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| import React, {Fragment} from 'react' import { useState, useEffect, useCallback, useMemo } from 'react' import { observer } from 'mobx-react'
const nameList = ['apple', 'peer', 'banana', 'lemon'] const Example = observer((props) => { const [price, setPrice] = useState(0) const [name, setName] = useState('apple') function getProductName() { console.log('getProductName触发') return name } useEffect(() => { console.log('name effect 触发') getProductName() }, [name]) useEffect(() => { console.log('price effect 触发') }, [price]) const memo_getProductName = useMemo(() => { console.log('name memo 触发') return () => name }, [name])
return ( <Fragment> <p>{name}</p> <p>{price}</p> <p>普通的name:{getProductName()}</p> <p>memo化的:{memo_getProductName ()}</p> <button onClick={() => setPrice(price+1)}>价钱+1</button> <button onClick={() => setName(nameList[Math.random() * nameList.length << 0])}>修改名字</button> </Fragment> ) }) export default Example
|
同样两个问题
- 点击价钱+1按钮会发生什么
1 2
| > getProductName触发 > price effect 触发
|
- 首先
DOM
改变,触发在p
标签中的getProductName
函数
- 然后调用
effect
显然我们已经成功的控制了触发(修改了显示price
的dom
,但是没有触发memo_getProductName
,没有输出’’name memo 触发’’),
这也是官方为什么说不能在useMemo
中操作DOM
之类的副作用操作,不要在这个函数内部执行与渲染无关的操作,诸如副作用这类的操作属于 useEffect 的适用范畴,而不是 useMemo,你可以试一下,在useMemo
中使用setState
你会发现会产生死循环,并且会有警告,因为useMemo
是在渲染中进行的,你在其中操作DOM
后,又会导致memo
触发
- 点击修改名字按钮会发生什么
1 2 3 4
| > name memo 触发 > getProductName触发 > name effect 触发 > getProductName触发
|
- 首先
DOM
变化,触发name
的memo
,
- 然后触发
p
标签内的getProductName
函数
DOM
操作结束后触发name
的effect
- 在
name
的effect
中触发getProductName
从这里也可以看出,useMemo
是在**DOM
更新前触**发的,useEffect
是在DOM
更新后触发的就像官方所说的