useEffect
An effect is a function tied to a specific component which runs on specific events or conditions.
non-blocking
The effect never delays the component being painted on-screen: neither the first paint nor the subsequent ones. Instead, effects run after the first paint and, if eligible, run after subsequent re-renders. This pattern aims to prioritize time-to-paint and time-to-render.
If the effect fetches data to be displayed on-screen, we have to deal with a first paint which doesn't have such data. We either use a default value, or hide the UI altogether and display a loader, a skeleton or nothing.
one or multiple runs
An effect runs at least once, after the first paint. The subsequent runs are conditionals. We can ask for the effect to run:
- after every render
- after renders where one or several variables changed in value.
dependencies
useEffect(effect, []) // once
useEffect(effect, [x]) // once, then when x changes
useEffect(effect) // on every render
clean-up on unmount
The component can unmount at any time. We perform clean-up in certain conditions:
- we have set-up a subscription and we want to cancel it.
- we have initiated a network fetch, and we want to disable the callback. We do that by setting a flag such as
isStaleto false, and by ensuring the callback doesn't mutate state when such flag is false.
clean-up on dependency change
The dependency change also triggers the clean-up function. That is why network fetches should also cancel when the effect changes in nature because of the dependency change, so we can use the term isStale instead of isMounted.
synopsis
useEffect(f, [])
synchronous function
Even though the effect can start asynchronous tasks, it must itself come as a non-async function, that is, it must run and return immediately. As such, we may not use await directly in its body, but we may define an async function which uses await in its body and call that function instead.
function myEffect() {
/* effect content */
return /* clean up content */
}