目录
1、第一个参数:
2、第二个参数:
2.1 不传值:无限循环
2.2 空数组作为依赖:执行一次
2.3 基本类型作为依赖:无限循环
2.4 引用类型
2.4.1 数组作为依赖:无限循环
2.4.2 函数作为依赖:无限循环
2.4.3 对象作为依赖:无限循环
1、第一个参数:
是一个
函数
,必传项。是组件要执行的副作用。可以看做componentDidMount
,componentDidUpdate
和componentWillUnmount
这三个函数的组合。const [count, setCount] = useState(0); useEffect(() => { // 普通函数,执行副作用,可以实现componentDidMount、componentDidUpdate console.log('执行副作用'); // return函数, 组件销毁时清除副作用,可以实现componentWillUnmount return () => { console.log("清除副作用"); }; }, [count]);
2、第二个参数:
可以
不传
或者是一个数组
,非必传项。数组里面依赖改变时候副作用函数才会重新更新。所谓依赖改变就是 对比 [ 之前值 === 之后值 ] ,如果为
true不执行useEffect
,为false重新执行useEffect
第二个参数类型:不传、[]、
由基本类型或者引用类型组成的数组
2.1 不传值:无限循环
【现象】: useEffectuseEffectuseEffect 会在第一次渲染以及每次更新渲染后都执行
【原因】:
第一次渲染后执行一次useEffect,useEffect中回调函数改变state值,state值改变触发组件重新渲染,useEffect没有比较值,useEffect重新执行,useEffect中回调函数改变state值,state值改变触发组件重新渲染,无限循环
注意:不传值是一种
缺失依赖关系
的情况,不建议这么做。const [count, setCount] = useState<number>(1); useEffect(() => { setTimeout(() => { setCount(count + 1); }, 1000); console.log(`第二个参数: 不传值, 第 ${count} 次执行`); }); // 打印log,无限循环 第二个参数: 不传值, 第 1 次执行 第二个参数: 不传值, 第 2 次执行 第二个参数: 不传值, 第 3 次执行 第二个参数: 不传值, 第 ... 次执行
2.2 空数组作为依赖:执行一次
【现象】: useEffect 会在第一次渲染后执行一次
【原因】: 第一次渲染后执行一次一次useEffect,useEffect中回调函数改变state值,state值改变触发组件重新渲染,useEffect中 [] 没有值,依赖没变,不触发useEffect,不执行回调函数, state 无更新,不触发组件重新渲染,至此结束
const [count, setCount] = useState<number>(1); useEffect(() => { setTimeout(() => { setCount(count + 1); }, 1000); console.log(`第二个参数: 空数组, 第 ${count} 次执行`); }, []); // 打印log,执行一次 第二个参数: 空数组, 第 1 次执行
2.3 基本类型作为依赖:无限循环
基本类型有:整型、浮点型、布尔型(true,false)、字符型、字符串、空值或null(Null)
【现象】: useEffect 会在第一次渲染以及每次更新渲染后都执行。
【原因】: 第一次渲染后执行一次useEffect,useEffect中回调函数改变state值,state值改变触发组件重新渲染,useEffect比较值(count)改变,useEffect重新执行,useEffect中回调函数改变state值,state值改变触发组件重新渲染,无限循环。
注意:传入第二个参数,只有
一个值
,比较该值有变化就执行,如果有多个值
的数组,会比较每一个值,有一个变化就执行const [count, setCount] = useState<number>(1); // 基本类型以number为例 useEffect(() => { setTimeout(() => { setCount(count + 1); }, 1000); console.log(`第二个参数: 基本类型, 第 ${count} 次执行`); }, [count]); // 打印log,无限循环 第二个参数: 基本类型, 第 1 次执行 第二个参数: 基本类型, 第 2 次执行 第二个参数: 基本类型, 第 3 次执行 第二个参数: 基本类型, 第 ... 次执行
2.4 引用类型
2.4.1 数组作为依赖:无限循环
【现象】:useEffect 会在第一次渲染以及每次更新渲染后都执行。
【原因】:第一次渲染后执行一次useEffect,useEffect中回调函数改变state值,state值改变触发组件重新渲染,useEffect依赖项arr发生变化,此处依赖数组执行
浅层比较
([...] === [...] 为false
)useEffect重新执行,useEffect中回调函数改变state值,state值改变触发组件重新渲染,无限循环
。const [count, setCount] = useState(1); const newArr = [4,5]; useEffect(() => { setTimeout(() => { setCount(count+1); }, 1000); console.log(`第二个参数: 数组, 第 ${count} 次执行`); }, [newArr]); // 打印log,无限循环 第二个参数: 数组, 第 1 次执行 第二个参数: 数组, 第 2 次执行 第二个参数: 数组, 第 3 次执行 第二个参数: 数组, 第 ... 次执行
【测试】:去除setTimeout会出现什么情况?---无限循环
因为useEffect频繁调用setState,state不断改变
const [count, setCount] = useState(1); const newArr = [4,5]; useEffect(() => { setCount(count+1); console.log(`第二个参数: 基本类型, 第 ${count} 次执行`); }, [newArr]); // 打印log报错,说:超出最大更新深度
【解决】: 使用useRef,useRef会在每次渲染时返回同一个ref对象,返回的
ref
在组件的整个生命周期内保持不变
const [count, setCount] = useState(1); const refArr = useRef([4, 5, 6]); useEffect(() => { setCount(count+1); console.log(`第二个参数: 数组, 第 ${count} 次执行`); }, [refArr.current]); // 打印log,执行一次 第二个参数: 数组, 第 1 次执行
2.4.2 函数作为依赖:无限循环
【现象】:useEffect 会在第一次渲染以及每次更新渲染后都执行。
【原因】:第一次渲染后执行一次useEffect,useEffect中回调函数改变state值,state值改变触发组件重新渲染,useEffect依赖项consoleFunction函数发生变化,此处依赖函数执行
浅层比较
(每次渲染都重新创建一个新的函数function(前) === function(后)为false
)useEffect重新执行,useEffect中回调函数改变state值,state值改变触发组件重新渲染,无限循环
const [count, setCount] = useState(1); const consoleFunction = () => { console.log('consoleFunction'); }; useEffect(() => { setTimeout(() => { setCount(count + 1); }, 1000); console.log(`第二个参数: 函数, 第 ${count} 次执行`); }, [consoleFunction]); // 打印log,无限循环 第二个参数: 函数, 第 1 次执行 第二个参数: 函数, 第 2 次执行 第二个参数: 函数, 第 3 次执行 第二个参数: 函数, 第 ... 次执行
【测试】:去除setTimeout会出现什么情况?---无限循环
因为useEffect频繁调用setState,state不断改变
打印报错:“ 超出最大更新深度 ”
【解决】: 使用useCallback,useCallback返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新
const [count, setCount] = useState(1); const consoleFunction = useCallback(() => { console.log('consoleFunction'); }, []); useEffect(() => { setCount(count + 1); console.log(`第二个参数: 函数, 第 ${count} 次执行`); }, [consoleFunction]); // 打印log,执行一次 第二个参数: 函数, 第 1 次执行
2.4.3 对象作为依赖:无限循环
【现象】:useEffect 会在第一次渲染以及每次更新渲染后都执行。
【原因】:第一次渲染后执行一次useEffect,useEffect中回调函数改变state值,state值改变触发组件重新渲染,useEffect依赖项obj发生变化,此处依赖对象执行
浅层比较
({...}=== {...} 为false
)useEffect重新执行,useEffect中回调函数改变state值,state值改变触发组件重新渲染,无限循环const [count, setCount] = useState(1); const obj = {name: 'zhangsan'}; useEffect(() => { setTimeout(() => { setCount(count + 1); }, 1000); console.log(`第二个参数: 对象, 第 ${count} 次执行`); }, [obj]); // 打印log,无限循环 第二个参数: 对象, 第 1 次执行 第二个参数: 对象, 第 2 次执行 第二个参数: 对象, 第 3 次执行 第二个参数: 对象, 第 ... 次执行
【测试】:去除setTimeout会出现什么情况?---无限循环
因为useEffect频繁调用setState,state不断改变
打印报错:“ 超出最大更新深度 ”
【解决】: 使用useMemo,useMemo该回调函数仅在某个依赖项改变时才会更新。此处使用[ ]依赖,组件重新渲染后对象不再重新定义
const [count, setCount] = useState(1); const obj = useMemo(() => ({name: 'zhangsan'}), []); useEffect(() => { setCount(count + 1); console.log(`第二个参数: 对象, 第 ${count} 次执行`); }, [obj]); // 打印log 第二个参数: 对象, 第 1 次执行