I’m currently learning about the cleanup function in useEffect but I’m stuck on how it works in this particular example. I’m specifically stuck in the order of operations.
Looking at the last three logs (at the bottom) after clicking the button, how does it go from running the effect for id = 3 where ignore = false --> back to ignore = true for id = 2 --> then into id = 3 and pokemon is set in state?
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>
)
}
LOGS:
On initial render:
Effect ran | id: [1]
Pokemon was set in state | id: [1]
After click the forward arrow twice fast (triggers side effect):
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]
I tried writing notes going line by line in the code. I expected to encounter a bit of an "aha!" moment but nothing! I'm still confused.
These two logs are from the following: due to the first button press, effect 2 is about to run. First it cleans up effect 1, and then it runs effect 2. As part of executing effect 2, it creates a local variable
ignore(set tofalse), and when it reaches theawait, execution pauses.Due to the second button press effect 3 is about to run, so it first cleans up effect 2. This sets effect 2's
ignorevariable totrue. It then starts executing effect 3. As before, this creates a localignorevariable, then executes until theawait.Note that while the variable has the same name "ignore", it's a different spot in memory and in a different closure. Effect 3's code can only interact with effect 3's ignore variable, and Effect 2 only interacts with Effect 2's ignore variable.
Eventually the promise from effect 2 finishes resolving, so effect 2 resumes execution. It checks its
ignorevariable and sees that it has been updated totrue. As a result, it does not try to set state.Eventually the promise from effect 3 finishes resolving, so effect 3 resumes execution. It checks its
ignorevariable and sees that it's stillfalse, so it sets the state.