关于useEffect
有有一些疑惑疑惑, 如下, 代码已经放入codesandbox 的 CounterHook.js 文件中
代码简化如下:
const CounterHook = props => {
const [count, setCount] = useState(0)
const [loading, setLoading] = useState(false)
useEffect(() => {
setLoading(true)
console.log(loading) // false, true, false, ture...
// 模拟发送数据
setTimeout(() => {
setLoading(false)
setCount(1)
}, 2000)
})
return ...
}
这里如果没有添加useEffect
的依赖数组, 会造成无限循环. 不是特别理解为什么会造成无限循环. 个人打印了一下 loading, 发现一直不断重复输出 false, true, false, true....
第一个问题
个人对造成无限循环的理解:
- 在调用
useEffect
之前, loading 是 false -
第一次调用
useEffect
:- 类似
componentDidMount
, 此时 loading 被设置成了 true, 同时发出了一个异步请求 - 请求结束, loading 又被设置成了 false, 组件的 state 被更新了, 由于
useEffect
在组件更新更新时也会触发, 因此触发第二次useEffect
:
- 类似
- 第二次调用
useEffect
: 和第一次一样, 首先componentDidMount
, 设置 loading 为 true...循环继续
所以第一个问题是, 不知道这样理解造成无限循环对不对?
第二个问题:
如果将请求数据部分抽成一个函数, 如下;
const CounterHook = props => {
const [count, setCount] = useState(0)
const [loading, setLoading] = useState(false)
useEffect(() => {
fetchApi()
}, [fetchApi])
const fetchApi = () => {
setLoading(true)
// 模拟发送数据
setTimeout(() => {
setLoading(false)
setCount(1)
}, 2000)
}
return ...
}
useEffect
的依赖数组变成了一个函数, 而且此时似乎仍旧会造成无限循环. 同时有提示:
The 'fetchApi' function makes the dependencies of useEffect Hook (at line 27) change on every render. Move it inside the useEffect callback. Alternatively, wrap the 'fetchApi' definition into its own useCallback() Hook. (react-hooks/exhaustive-deps)
所以第二个问题为: 如果useEffect
的依赖数组存在函数, 该如何理解这个函数是否"变化"? 例如上面例子里面, 为什么会造成无限循环, 函数到底是哪里"变化"了导致useEffect
不断的被重新调用?
第三个问题
和第二个问题有关联, 按照提示修改, 使用useCallback
包装了一下fetchApi
这个函数, 代码如下:
const CounterHook = props => {
const [count, setCount] = useState(0)
const [loading, setLoading] = useState(false)
useEffect(() => {
fetchApiCallback()
}, [fetchApiCallback])
const fetchApiCallback = useCallback(() => {
setLoading(true)
// 模拟发送数据
setTimeout(() => {
setLoading(false)
setCount(1)
}, 2000)
}, [])
return ...
问题: useCallback
依赖添加了一个依赖数组, 里面为空, 似乎代码没有出现无限循环的情况, 请问该如何解释? useCallback
到底使得函数的"变化发生了怎样的改变"?
以上是三个问题, 问题有些长与啰嗦, 打扰了各位前辈了. 如果能解答, 真的不胜感激, 谢谢!
第一个问题:
对。
第二个问题:
函数组件的每次渲染都是执行一遍这个函数,在你的例子中,也就是
CounterHook
函数。那么两次执行中创建的局部函数,还会是同一个引用吗?不会的。所以本次执行中的fetchApi
和上次执行中的fetchApi
是两个不同的函数,因此useEffect
的依赖被更新了,触发执行。第三个问题:
useCallback
的返回值即为它接收的第一个参数,直到依赖更新。因此使用useCallback
包裹的fetchApi
,在依赖更新之前,总是同一个函数。也就不会触发useEffect
。