An introduction on how to use useEffect Hook
What is a useEffect
Hook?
TLDR
React
useEffect
is an alternative to the "old"class
lifecycle methods/hooks.It can be used to manage side effects, such as network requests, or to run a piece of code when the component is mounted, updated, or unmounted.
Longer version
Prior to
React v16.8
, we can only enable a component
to react to state changes using lifecycle methods.
How to define a useEffect
useEffect(() => { //(1) declaration
// (2)effect
return () => {
// (3)cleanup
}
}, /* (4)dependency array */)
Here's an explanation of the above code:
- We can declare an effect by calling either
React.useEffect
oruseEffect
-
effect
is the function that will be called when the component is mounted OR when the dependency array changes. -
cleanup
is the function that will be called when the effect "unmounted". -
dependency array
is the array of values that will be passed to theeffect
function.- If there is no dependency array, the effect will be called every time the component is mounted.
- If the array is empty, the effect will be called only once when the component is mounted.
- If the array is not empty, the effect will be called every time the component is mounted and the dependency array changes.
Examples
Increment a counter every second until it reaches 10
function App() {
const [count, setCount] = useState(0)
useEffect(() => {
if (count < 10) {
const interval = setInterval(() => {
setCount(prev => prev + 1)
}, 1000)
// cleanup function
return () => clearInterval(interval)
}
}, [count])
// Render the component
}
Basic fetch from an API
function App() {
const [data, setData] = useState([])
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/users')
.then(res => res.json())
.then(data => setData(data))
}, [])
// Render the component
}
Fetching with loading indicator + error handling - then/catch/finally
function App() {
const [data, setData] = React.useState()
const [error, setError] = React.useState()
const [isLoading, setIsLoading] = React.useState(false)
React.useEffect(() => {
setIsLoading(true)
fetchData()
.then(data => {
setError(null)
setData(data)
})
.catch(data => {
// handle error case anyway you want
setError(data)
setData(null)
})
.finally(() => setIsLoading(false))
}, [])
// Render the component
}
Fetching with loading indicator + error handling - async/await
function App() {
const [data, setData] = React.useState()
const [error, setError] = React.useState()
const [isLoading, setIsLoading] = React.useState(false)
React.useEffect(() => {
// yeah, this is weird
(async () => {
try {
setIsLoading(true)
const data = await fetchData()
setError(null)
setData(data)
} catch(e) {
// handle error case anyway you want
setError(e)
setData(null)
}
setIsLoading(false)
})()
}, [])
// Render the component
}
Store a value in localStorage everytime the key or value changes
function App({ name }) {
const [value, setValue] = useState(() => localStorage.getItem(name))
useEffect(() => {
localStorage.setItem(name, value)
}, [name, value])
// Ignore old keys for now
// Render the component
}
OR mimic a class lifecycle method
Check this blog on how to convert a class lifecycle methods to
useEffect
hooks
Additional Note
-
useEffect
can only be used in functional components - The order of
useEffect
declarations are important. -
useEffect
in a custom hook is a great way to promote side effect reusability. I will discuss this in another blog.
Conclusion
Compared to the old lifecycle methods, useEffect
is much more powerful and flexible, making it an ideal choice when managing a side-effect.
Top comments (0)