背景
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更新后触发的就像官方所说的