react推出的,用于执行副作用的钩子
在react组件中, 执行数据请求 、 dom操作,统一称之为副作用
useEffect接收两个参数,第一个是执行函数,第二个是依赖数组
import React, { useEffect, useRef, useState } from 'react'
const ClickCount = () => {
const [count, setCount] = useState(0)
const preObj = useRef({a: 1})
const [obj, setObj] = useState(preObj.current)
useEffect(() => {
console.log('excuted')
})
return (
{count}
)
}
export default ClickCount
有如上这段代码, 当每次点击button时,都会执行setCount函数,count发生改变,由于此时useEffect没有传入第二个参数,所以excuted 会被打印。
如果给useEffect第二个参数传入一个空数组,则此时useEffect只会在组件初始化的时候执行一次,我认为接口调用放在这里执行比较合适。
若数组不为空,比如 [count], 则 useEffect 会在count发生改变的时候执行, 有点类似于 vue中的watch监听。
以上都没有太大问题,但是很多情况下,我们会需要在依赖中传入对象,那么此时就会发生问题。
useEffect需要将此次渲染时,依赖中的值与上次渲染依赖中的值做对比, 如果结果为true, 表示两者一致,未发生改变,故而不需要再次渲染, 若结果为false,表示发生了改变,会重新调用执行函数。并且,useEffect的比较为浅比较,查阅资料之后发现是调用Object.is(arg1, arg2),若参数为基本类型,则不会有问题,但是如果为引用类型, 则比较的是两者的地址,而非值, 即 : Object.is({a: 1}, {a: 1})的结果为false, 会再次调用执行函数,这就是依赖最好不要传入引用类型的原因,并且,如果在useEffect中修改了引用类型,则会引发无限渲染的问题
import React, { useEffect, useRef, useState } from 'react'
import useDeepCompareEffect from 'use-deep-compare-effect'
const ClickCount = () => {
const [count, setCount] = useState(0)
const [obj, setObj] = useState({a: 1})
useDeepCompareEffect(() => {
console.log('excuted')
}, [obj])
return (
{count}
)
}
export default ClickCount
查阅资料之后也可以使用 useRef这个钩子来解决上述问题,useRef的特性是跨渲染周期保存数据,
代码如下
import React, { useEffect, useRef, useState } from 'react'
import useDeepCompareEffect from 'use-deep-compare-effect'
function usePrevious(value: any) {
const ref = useRef()
useEffect(() => {
ref.current = value
}, [value])
return ref.current
}
const ClickCount = () => {
const [count, setCount] = useState(0)
const [obj, setObj] = useState({a: 1})
const prevObj = usePrevious(obj)
// useDeepCompareEffect(() => {
// console.log('excuted')
// }, [obj])
useEffect(() => {
prevObj && prevObj.a < obj.a && console.log('do something')
}, [obj])
return (
{count}
)
}
export default ClickCount
这里用useRef保存了之前的数据, useEffect中的依赖以然为引用类型, 每次obj发生改变都会调用执行函数,但是执行函数中多了一个判断, prevObj是上一次渲染时obj的值, 用prevObj中的某个key与此次obj中的某个key做对比,满足条件后做其他操作,这也是解决方法之一。
但是个人更推荐第一种,直接使用现有的库,更加方便
但是相对的,第一种方案,有可能是进行了深比较,可能是用递归的方式进行比较的,那么当出现 {a: {b: {c: {d: 1}}}}这种多层嵌套的情况时,性能有可能会不那么好。故而还需看具体的应用场景。