本文可能会让你感觉到很啰嗦,我想尽量线索清晰,但是好像有点失败...
1. 背景
先看下面这段代码
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 |
现在问几个问题:
发生下面几种情况会重新渲染界面吗(也就是
- 点击价钱+1按钮?
- 点击修改名字按钮?
很显然在进行
不控制重复渲染容易产生一些奇怪的问题
1
2
3
4 // 假设在上面函数组件里面有一个定时任务
setInterval(() => {
console.log('触发了定时器')
}, 5000)当你点击修改价格或者修改名字的时候,每次都会触发渲染,说白了就是将这个函数组件的函数,再执行一次,显然又会添加一个定时器, 这时就容易产生内存泄漏,当然你也可以在组件外部定义一个变量保存定时器
id
2. 使用什么方法解决
2.1 useEffect ?
最开始遇到这个问题时,由于我刚接触
1 2 3 4 | // 将上面的函数用useEffect包裹,并设置依赖,只有name发生变化的时候才触发 useEffect(() => { getProductName() }, [name]) |
很明显,我犯了一个错误,我这样控制只是在渲染前控制,而上面提到的重新渲染控制,需要在渲染期间控制,也就是在
看下面一段代码
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 } // 只对name响应 useEffect(() => { console.log('name effect 触发') getProductName() }, [name]) // 只对price响应 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 |
- 若点击价钱+1按钮会打印什么?
1 2 | > getProductName触发 > price effect 触发 |
为什么是这个顺序:
- 官方文档有说过 当你调用 useEffect 时,就是在告诉 React 在完成对 DOM 的更改后运行你的“副作用”函数
- 显然当我使用
setPrice 的时候,产生DOM 操作,从而触发了在p 标签中的getProductName 函数 - 然后调用副作用触发了
name 的effect
- 若点击修改名字按钮会打印什么?
1 2 3 | > getProductName触发 > name effect 触发 > getProductName触发 |
这个顺序就很好理解了
- 因为修改了
DOM ,触发了getProductName - 随后调用了
price 的effect effect 中调用了getProductName
我们的目标是在
2.2 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 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 } // 只对name响应 useEffect(() => { console.log('name effect 触发') getProductName() }, [name]) // 只对price响应 useEffect(() => { console.log('price effect 触发') }, [price]) // memo化的name属性 ?????? const memo_name = useMemo(() => { console.log('name memo 触发') return () => name // 返回一个函数 }, [name]) return ( <Fragment> <p>{name}</p> <p>{price}</p> <p>普通的name:{getProductName()}</p> <p>memo化的:{memo_name()}</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
显然我们已经成功的控制了触发(没有触发
- 点击修改名字按钮会发生什么
1 2 3 4 | > name memo 触发 > getProductName触发 > name effect 触发 > getProductName触发 |
- 首先
DOM 变化,触发name 的memo , - 然后触发
p 标签内的getProductName 函数 DOM 操作结束后触发name 的effect - 在
name 的effect 中触发getProductName
从这里也可以看出,
作者:头上有煎饺
链接:https://www.jianshu.com/p/94ace269414d
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。