好吧,useEffect会用就行

前言

之前也看过关于useEffect的文章,但是真到自己使用,或者叫我讲出为啥的时候,我是真讲不出来。但是有些项目却总是开启了useEffect依赖检测,如果依赖没有使用正确的话控制台会warning或者直接报错。没办法,虽然我记不住啥啥啥原理,我知道怎么用就好了。虽然说是这么说,但是基础的东西是要知道的

前提

请开启useEffect依赖规则,如果不开启配置的话,我们有时候甚至不会去关注useEffect依赖问题。 yarn add eslint-plugin-react-hooks --dev

然后在.eslintrc.json文件中,引入规则。

1.在plugin: [“react-hooks”]2.在rules: { “react-hooks/rules-of-hooks”: “warn”, // 检查 Hook 的规则 “react-hooks/exhaustive-deps”: “warn”, // 检查 effect 的依赖 }如何使用webpack配置react项目可以看我之前写的文章。上面的配置也是写在之前写文章时用的项目中。useEffect是用来干什么的?

useEffect本身就是一个函数,接受一个函数作为参数,然后这个函数会在组件渲染到屏幕之后执行。由于是每轮渲染完后执行,所以它可以做一下改变DOM、添加订阅、设置定时器,请求接口数据等有副作用的操作。因为useEffect是放到组件里面,所以组件的每次更新理论上来讲都会生成一个新的useEffect并且执行,但是我们可以有选择性的让他执行,这个就是我们需要讲的依赖。反正我用来请求接口居多…

没有依赖

不需要依赖就是useEffect中传入一个函数作为第一个参数,第二个参数不传。这样每次组件状态更新useEffect都会更新(更新前会记录当前的状态)并且执行,意思就是不会被当前状态影响。如下

const [count, setCount] = useState(0); useEffect(() => { console.log('count', count); }) 

如果没有依赖,每次count改变,都会打印最新的count。

前面一直说,每次更新都会一个新的useEffect,并且更新前会记录当前的状态。例子如下

const Dashboard = () => { const [count, setCount] = useState(0); const handleClick = () => { setCount(count + 1) } useEffect(() => { setTimeout(() => { console.log('count', count); }, 2000) }) return ( <> ---{count}--- <button onClick={handleClick}>点击+1</button> </> );
} 

如果我连续点击3次button是count加1,大家可以思考下,setTimeout里面输出的是什么样子的?是0,1,2,3?还是全都是3.答案是前者,因为组件中的函数(包括useEffect)会捕获每次状态更新时的状态。

有依赖

依赖以数组的方式作为useEffect的第二个参数。当依赖发生改变useEffect会再次执行。下面有几个例子是当使用依赖报错时的解决办法

场景一: []作为依赖

[]作为依赖,原意是为了让页面一挂载就执行,并且只执行一次。但有时候这样往往不尽人意。

 useEffect(() => {console.log('页面挂载我就渲染,组件状态改变我是不会渲染的');}, []); 

场景二:页面一挂载就开启倒计时

强行举个场景例子。例如一进入这个页面,我就开启一个倒计时(我只想运行一次useEffect),倒计时过后退出这个页面。你可能会这样写

import React, {useEffect,useState
} from 'react';
const Dashboard = () => {const [count, setCount] = useState(60);useEffect(() => {const timer = setInterval(() => {setCount(count -1)}, 1000)return () => clearInterval(timer)}, [])return (<>---{count}---</>);
}

export default Dashboard 

这样写,如果开起来依赖报错,那么编辑器肯定就会提醒你依赖错误,如

抛开这个报错,你认为count会如何变化。运行下代码就知道了,count变为59就不会变了。因为由于useEffect依赖设置为[],所以只会执行一次,然后加上useEffect会记录之前的状态,所以记录的是count为60的状态。于是每隔一秒钟setInterval执行的时候count都是60,所以页面会一直显示59如果你根据提示添加count作为依赖。首先效果可以达到,但不是很理想。分析一下,如果count作为依赖,第一次count为60,然后useEffect开始执行,每隔一秒钟count减1,即count一直再变。那么根据依赖count在变,useEffect会重新执行,每次新建一个定时器,然后再次渲染之后又会清除前面一个定时器再新建一个。虽然能实现效果,但是不完美。比较正确的做法就是尽量不依靠依赖,尽量不要在useEffect中使用组件里面的状态。比如说这个count我们其实可以不依赖这个count,我们可以这样做

useEffect(() => { const timer = setInterval(() => { setCount(prev => prev - 1) }, 1000) return () => clearInterval(timer) }, []) 

场景三:函数作为依赖

函数作为依赖第一印象是不行的,因为组件每次更新同一个函数都会重新生成,并且与之前都不一样,所以如果单纯的把函数作为依赖,那么useEffect会无限调用。有这样的场景,页面一挂载完成就要去请求数据.

1.单独写一个方法,然后放到useEffect中,这个方面不适用任何一个组件中的状态如下处理,也可以把fetchData方法提出来,放到组件外面,这样也可以减少每次组件渲染重复加载这个方法。

import React, {useEffect,useState
} from 'react';
import Axios from 'axios';
const Dashboard = () => {const [userId, setUserId] = useState(101);const [data, setData] = useState<any[]>([]);const fetchData = async () => {const result = await Axios({headers: {'Content-type': 'application/json; charset=UTF-8',},method: 'post',url: 'https://jsonplaceholder.typicode.com/posts',data: JSON.stringify({title: 'foo',body: 'bar',userId: 1})}).then((res: any) => {console.log('res', res);return []});setData(result)}useEffect(() => {fetchData()}, []);return (<>---{count}---</>);
}

export default Dashboard 

2.单独写一个方法,但是需要用到组件中的状态

如果你直接把userId,如下直接放入fetchData方法中,那么编辑器会报错,会提醒你需要fetchData这个方法作为依赖。好,然后我们就继续把fetchData 这个方法作为useEffect的依赖

import React, {useEffect,useState
} from 'react';
import Axios from 'axios';
const Dashboard = () => {const [userId, setUserId] = useState(101);const [data, setData] = useState<any[]>([]);const fetchData = async () => {const result = await Axios({headers: {'Content-type': 'application/json; charset=UTF-8',},method: 'post',url: 'https://jsonplaceholder.typicode.com/posts',data: JSON.stringify({title: 'foo',body: 'bar',userId: userId})}).then((res: any) => {console.log('res', res);return []});setData(result)}useEffect(() => {fetchData()}, []);return (<>---{userId}---</>);
} 

第二步 把fetchData作为依赖,这就比较坑了。如果你没有开启依赖报错,那么页面已刷新会一直执行featData这个方法。分析下,因为fetchData在每次组件更新时,都会生成新的fetchData与之前fetchData不一样,所以useEffect就会不断执行。怎么解决这个问题嘞,我们可以使用useCallback(因为useCallback第二个参数也可以传递依赖,当这个依赖发生变化时,fetchData才会重新执行)

useEffect(() => {fetchData()
}, [fetchData]); 

第三步 用useCallback去包裹fetchaData。如下,这样就可以了,只有userId发生改变的时候useEffect才会重新执行

const fetchData = useCallback(async () => {const result = await Axios({headers: {'Content-type': 'application/json; charset=UTF-8',},method: 'post',url: 'https://jsonplaceholder.typicode.com/posts',data: JSON.stringify({title: 'foo',body: 'bar',userId: userId})// data: {a: count}}).then((res: any) => {console.log('res', res);return []});setData(result)}, [userId]) 
函数做依赖总结

1.如果这个方法不需要状态(state),那么可以把这个方法单独提出这个组件。
2.如果需要state且这个方法不需要被复用,可以把这个方法放到useEffect中,然后把这个state作为useEffect的依赖.

 useEffect(() => {const fetchData = async () => {const result = await Axios({headers: {'Content-type': 'application/json; charset=UTF-8',},method: 'post',url: 'https://jsonplaceholder.typicode.com/posts',data: JSON.stringify({title: 'foo',body: 'bar',userId: userId})// data: {a: count}}).then((res: any) => {console.log('res', res);return []});setData(result)}fetchData()}, [userId]); 

3.如果需要state且这个方法不需要被复用.把这个方法提出去,然后把state作为参数传递给这个方法。然后把这个方法放到useEffect中,最后把这个state作为依赖

// 这个方法放到组件外部
const fetchData = async (id: number) => {const result = await Axios({headers: {'Content-type': 'application/json; charset=UTF-8',},method: 'post',url: 'https://jsonplaceholder.typicode.com/posts',data: JSON.stringify({title: 'foo',body: 'bar',userId: id})// data: {a: count}}).then((res: any) => {console.log('res', res);return []});// setData(result)return result
}

useEffect(() => { fetchData(userId)}, [userId]); 

4.需要state,且这个方法在组件中还有别的地方调用。可以在组件中生命这个方法,且使用useCallback包裹着,并让使用的state作为useCallback的依赖项,最后在useEffect中调用这个方法,并使这个方法称为useEffect的依赖。

 import React, {useCallback,useEffect,useState
} from 'react';
import Axios from 'axios';
const Dashboard = () => {const [userId, setUserId] = useState(60);const [data, setData] = useState<any[]>([]);const fetchData = useCallback(async () => {const result = await Axios({headers: {'Content-type': 'application/json; charset=UTF-8',},method: 'post',url: 'https://jsonplaceholder.typicode.com/posts',data: JSON.stringify({title: 'foo',body: 'bar',userId: userId})// data: {a: count}}).then((res: any) => {console.log('res', res);return []});setData(result)}, [userId])useEffect(() => {fetchData()}, [fetchData]);return (<>---{userId}---</>);
}

export default Dashboard 

其实函数作为依赖,主要是得注意函数每次状态更新都会生成不一样的函数,每次不一样的话相当于这个依赖无效,所以要保证函数的不变性。

场景四:props作为依赖

prop作为依赖其实和state一样的,按照上述的解决办法就行。

总结

个人感觉useEffect还不够智能,不能实现不需要使用依赖就能自己帮我们做处理,必须我们自己去设置。不过具体为什么这么去实现,我还没有深究。以上也是我在使用useEffect过程中遇到的问题,以及解决办法,我也不需要去理解useEffect,会用就行。ps:理解了还是会忘记,烦!!!

最后

最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值