useEffect.dev
← Back to lessons

Lesson 2. Trigger side effects with useEffect 2. Handling asynchronous effects and clean up

Not only we want to log the title two seconds after it is updated, but in the LogTitle component itself, we also want to display the message at the same time. This means that we have to use a local state:

const DelayedLogTitle = ({ title }) => {
const [delayedTitle, setDelayedTitle] = useState('')
useEffect(() => {
setTimeout(() => {
console.log('New title:', title)
setDelayedTitle(title)
}, 2000)
}, [title])
return <p>Logging in 2 seconds: {delayedTitle}</p>
}

This will be working fine, but there might be an issue if the component is unmounted between the time the title is updated and the time we update delayedTitle accordingly. In that case, we would call setDelayedTitle when the component is unmounted, and React will display a warning in the console.

To fix this issue, useEffect allows us to return a clean-up function in the callback: this function will be executed:

  • when one of the values in the dependencies array is updated, just before triggerring a new rendering,
  • when the component is unmounted.

In our case we want to update the state using setDelayedTitle only if the component is still mounted. Here, the good news is that there is a way to cancel a timeout, giving us a first way to proceed, using clearTimeout in the clean up function:

useEffect(() => {
const timeout = setTimeout(() => {
console.log('New title:', title)
setDelayedTitle(title)
}, 2000)
return () => {
clearTimeout(timeout)
}
}, [title])

Another way to do is to set a flag to true in the useEffect, set it to false in the clean up function, and updating the state only if the flag is still true. For instance, here we want to log the title in the console without updating the state:

useEffect(() => {
let active = true
setTimeout(() => {
console.log('New title:', title)
if (active) {
setDelayedTitle(title)
}
}, 2000)
return () => {
active = false
}
}, [title])

This way of doing using a flag is useful when the async action cannot be cancelled (as you can cancel a setTimeout or setInterval), for instance when you use async functions, i.e. promises.

Now that we know how to use useEffect, let’s see how to use it to do something very common: making HTTP requests.