我目前正在学习 useEffect 中的清理函数,但我对它在这个特定示例中的工作原理很困惑。我特别受困于操作顺序。
查看单击按钮后的最后三个日志(底部),它是如何从运行效果 for id = 3 whereignore = false --> 返回到ignore = true for id = 2 --> 然后进入id = 3 并且 pokemon 已设置为状态?
import * as React from "react"
import { fetchPokemon } from "./api"
import Carousel from "./Carousel"
import PokemonCard from "./PokemonCard"
export default function App () {
const [id, setId] = React.useState(1)
const [pokemon, setPokemon] = React.useState(null)
const [loading, setLoading] = React.useState(true)
const [error, setError] = React.useState(null)
const handlePrevious = () => {
if (id > 1) {
setId(id - 1)
}
}
const handleNext = () => setId(id + 1)
React.useEffect(() => {
let ignore = false
console.log(`Effect ran | id: [${id}]`)
const handleFetchPokemon = async () => {
setLoading(true)
setError(null)
const { error, response } = await fetchPokemon(id)
if (ignore) {
console.log(`ignore is: [${ignore}] | id: [${id}]`)
return
} else if (error) {
setError(error.message)
} else {
console.log(`Pokemon was set in state | id: [${id}]`)
setPokemon(response)
}
setLoading(false)
}
handleFetchPokemon()
return () => {
console.log(`cleanup function ran | id: [${id}]`)
ignore = true
}
}, [id])
return (
<Carousel onPrevious={handlePrevious} onNext={handleNext}>
<PokemonCard
loading={loading}
error={error}
data={pokemon}
/>
</Carousel>
)
}
日志:
初始渲染时:
Effect ran | id: [1]
Pokemon was set in state | id: [1]
快速单击向前箭头两次后(触发副作用):
cleanup function ran | id: [1]
Effect ran | id: [2]
cleanup function ran | id: [2]
Effect ran | id: [3]
ignore is: [true] | id: [2]
Pokemon was set in state | id: [3]
我尝试在代码中逐行写注释。我预计会遇到一些“啊哈!”的情况。瞬间却什么也没有!我还是很困惑。
清理功能运行 |编号:[1]
运行效果|编号:[2]
这两条日志来自于:由于第一次按下按钮,效果2即将运行。首先它清理效果 1,然后运行效果 2。作为执行效果 2 的一部分,它创建一个局部变量
ignore
(设置为 false
),当它到达 await
时,执行暂停。
清理功能运行 |编号:[2]
运行效果|编号:[3]
由于按下第二个按钮,效果 3 即将运行,因此它首先清理效果 2。这会将效果 2 的
ignore
变量设置为 true
。然后它开始执行效果 3。和之前一样,这会创建一个本地 ignore
变量,然后执行直到 await
。
请注意,虽然变量具有相同的名称“ignore”,但它在内存中处于不同的位置并且位于不同的闭包中。 Effect 3 的代码只能与 Effect 3 的忽略变量交互,而 Effect 2 只能与 Effect 2 的忽略变量交互。
忽略是:[true] |编号:[2]
最终,效果 2 的 Promise 完成解析,因此效果 2 恢复执行。它检查其
ignore
变量并发现它已更新为 true
。因此,它不会尝试设置状态。
神奇宝贝已设置为状态 |编号:[3]
最终,效果 3 的 Promise 完成解析,因此效果 3 恢复执行。它检查其
ignore
变量并发现它仍然是 false
,因此它设置了状态。