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)