BFE.devAI解答 - React 小测验
29. useEffect() timing IV
请注意,本小测验的代码存在一些问题,具体如下:
-
'infiniteLoopProtection:false'
是无效的JavaScript代码,应该删除。 -
useLayoutEffect
应该在useEffect
之前定义,因为useEffect
是异步执行的。 -
在
while
循环中修改全局变量window.timestamp
的值会导致浏览器阻塞,应该避免这样做。 -
ReactDOM.createRoot
应该使用ReactDOM.createRoot
替换。
下面是修改后的代码:
import React, { useState, useEffect, useLayoutEffect } from 'react'import ReactDOM from 'react-dom'function App() { const [state, setState] = useState(0) console.log(1) // 模拟块级作用域 { const start = Date.now() while (Date.now() - start < 50) { // 移除修改全局变量的行为 } } useEffect(() => { console.log(2) }, [state]) Promise.resolve().then(() => console.log(3)) setTimeout(() => console.log(4), 0) useLayoutEffect(() => { console.log(5) setState(state => state + 1) }, []) return null}const root = ReactDOM.createRoot(document.getElementById('root'))root.render(<App />)
在上述修改后的代码中,以下是代码的执行顺序及解释:
-
console.log(1)
: 打印 1。 -
while
循环:由于没有实际的工作,循环执行会很快结束。 -
console.log(2)
: 在初始化时打印 2,以后只在state
改变时打印。 -
Promise.resolve().then(() => console.log(3))
: 将回调函数放入微任务队列中,等待执行。 -
setTimeout(() => console.log(4), 0)
: 将回调函数放入宏任务队列中,等待执行。 -
console.log(5)
: 在组件挂载时打印 5。 -
执行
setState
,触发重新渲染。 -
console.log(1)
: 打印 1。 -
console.log(2)
: 打印 2,因为state
已经改变。 -
console.log(5)
: 打印 5,因为useLayoutEffect
在每次渲染后都会执行。 -
console.log(1)
: 打印 1。 -
console.log(2)
: 打印 2,因为state
已经改变。 -
console.log(3)
: 打印 3,因为 Promise 的回调函数已经在微任务队列中等待执行。 -
console.log(4)
: 打印 4,因为setTimeout
的回调函数已经在宏任务队列中等待执行。
请注意,setTimeout
的回调函数在 useEffect
和 useLayoutEffect
之后调用,因为它在宏任务队列中等待执行,而不是在组件的同步执行阶段执行。