useEffect这个hooks应该是我们平常在写react项目中使用频率最高的hooks之一了,它可以在依赖项改变时执行副作用函数,这是它最主要的功能。
1.当没有依赖项时,useEffect中的副作用函数只会执行一次。
const fn=()=>{}
useEffect(()=>{
fn();
},[count])
2.当有依赖项时,如果点击一次按钮,fn就会被调用一次,useEffect会对比前后两次的count,因为count我们定义的是基本类型,所以就是判断值相不相等。
const [count,setCount] = useState(0);
const fn=()=>{}
useEffect(()=>{
fn();
},[count])
return(
<>
<button onClick={()=>setCount(count+1)>
click
</button>
</>)
3.当useEffect的依赖是引用类型的时候。我们这里把依赖项换成fn这个函数,如果我们此时运行项目,会发现控制台会一直输出1。
const [count,setCount] = useCount(0);
const fn=()=>{};
useEffect(()=>{
console.log(1);
setCount(count+1);
},[fn]);
原因:
1.代码运行进useEffect中,依赖为fn,调用了setCount(),数据改变导致页面重新渲染,
2.代码从上到下执行,fn被重新定义了一遍,此时的fn不再是一开始的fn,所以第一次的fn和第二次的fn不是同一个fn,useEffect中的副作用函数被再次调用
3.setCount的再次调用,又改变了state,走第2步,所以造成了死循环。
如何解决?
想要知道如何解决这个问题,应该先要知道useEffect对比两次依赖的时候是如何对比的,通过翻阅资料,找到了对比依赖时,调用的是Object.is()方法,Object.is(a,b),如果a,b是基本数据类型,直接对比二者的值,不相等则返回false,或者返回true,这里是不会进行数据类型转换比较的。
如果a,b是引用数据类型,直接判断二者的地址是否相同,不相同则返回FALSE,否则返回true。
如果就是有需求,当fn改变时,去做一些操作,那么我们该如何处理这种情况呢?
解决办法
使用useCallback这个hook,useCallback是当某个依赖改变时,返回一个函数,在依赖没有改变时,useCallback会缓存第一次返回的函数。
const [count,setCount] = useCount(0);
const fn= useCallback(()=>{
const callFn = ()=>{
//to do
}
return callFn;
},[]);
useEffect(()=>{
console.log(1);
setCount(count+1);
},[fn]);
效果图:这样就不会出现死循环了。
如果我们需要依赖是一个对象呢?
那么我们需要使用的是useMemo这个hook,这个hook是帮助我们缓存一个数据的,useCallback是帮助我们缓存一个函数的。
const [count,setCount] = useCount(0);
const objDep= useMemo(()=>{
const obj = {name:"joker"};
return obj;
},[]);
useEffect(()=>{
console.log(1);
setCount(count+1);
},[objDep]);