useEffect 中清除陈旧效果的清理函数的操作顺序是什么?

问题描述 投票:0回答:1

我目前正在学习 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]

我尝试在代码中逐行写注释。我预计会遇到一些“啊哈!”的情况。瞬间却什么也没有!我还是很困惑。

reactjs react-hooks jsx code-cleanup
1个回答
0
投票

清理功能运行 |编号:[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
,因此它设置了状态。

© www.soinside.com 2019 - 2024. All rights reserved.