When building a React app, there will be times where we want to run a piece of code after a component is rendered (think fetching data from an api as one example) — the useEffect React Hook will help us accomplish that.
Prior to React Hooks, we would give our app this type of functionality by making use of lifecycle methods (componentDidMount, componentDidUpdate, and componentWillUnmount). The useEffect hook allows us to essentially roll those three methods up into one convenient function.
How It Works
The useEffect hook is just a function that takes in two arguments — a function that you want to run after a component is rendered and a dependency array. The dependency array is optional — I will explain this in more detail a bit further down — but the idea is that it will allow you to limit how often the function passed to useEffect is run. Be sure to import
How to Use It
- Import useEffect from React
- Add the useEffect function and pass it the function (and dependency array if needed — more info below)
One thing to be aware of is that for certain situations, you'll want to clean up your useEffect function — which is done by returning a cleanup function. You can read more about cleaning up after useEffect here and here or by watching this video.
Examples:
A simple counter example WITHOUT dependency array:
// 1. Import useEffect from React
import React, {useState, useEffect} from 'react';
import './App.css';
function App() {
const [count, setCount] = useState(0)
const [clicks, setClicks] = useState(0)
// 2. Add useEffect function and pass it the function
useEffect(() => {console.log(`Count was updated: count is ${count} and you've clicked the console.log button ${clicks} time(s)`)})
// Because there is no dependency array, the function inside useEffect will run everytime our App component is rendered, so essentially whenever count / clicks changes.
return (
<div>
{count}
<button onClick={() => setCount(currentCount => currentCount - 1)}>-</button>
<button onClick={() => setCount(currentCount => currentCount + 1)}>+</button>
<button onClick={() => setClicks(currentClicks => currentClicks + 1)}>console.log</button>
</div>
)
}
export default App;
Dependency Array
There are two ways you can make use of the dependency array:
- passing an empty array will only trigger the useEffect on the initial render of the component
- passing an array with a dependency (a variable, so in this case our count variable) — which will trigger the useEffect function ONLY when something in that dependency array changes (in this case our count variable)
A simple counter example WITH an empty dependency array:
// 1. Import useEffect from React
import React, {useState, useEffect} from 'react';
import './App.css';
function App() {
const [count, setCount] = useState(0)
const [clicks, setClicks] = useState(0)
// 2. Add useEffect function and pass it the function and empty dependency array
useEffect(() => {console.log(`Count was updated: count is ${count} and you've clicked the console.log button ${clicks} time(s)`)},[])
// Because there is an empty dependency array, the function inside useEffect will run after the initial render of our app, and thats it.
return (
<div>
{count}
<button onClick={() => setCount(currentCount => currentCount - 1)}>-</button>
<button onClick={() => setCount(currentCount => currentCount + 1)}>+</button>
<button onClick={() => setClicks(currentClicks => currentClicks + 1)}>console.log</button>
</div>
)
}
export default App;
A simple counter example WITH a dependency array that includes the count variable:
// 1. Import useEffect from React
import React, {useState, useEffect} from 'react';
import './App.css';
function App() {
const [count, setCount] = useState(0)
const [clicks, setClicks] = useState(0)
// 2. Add useEffect function and pass it the function and empty dependency array
useEffect(() => {console.log(`Count was updated: count is ${count} and you've clicked the console.log button ${clicks} time(s)`)},[count])
// Because we included count in the dependency array, the function inside useEffect will run everytime the value of count changes.
return (
<div>
{count}
<button onClick={() => setCount(currentCount => currentCount - 1)}>-</button>
<button onClick={() => setCount(currentCount => currentCount + 1)}>+</button>
<button onClick={() => setClicks(currentClicks => currentClicks + 1)}>console.log</button>
</div>
)
}
export default App;
Try It Yourself
It is always easier to see this type of functionality by trying it out yourself. Head over to the replit, fork the repo, and try out each of these useEffect examples by copying it into the replit. You likely wont be able to see the console.log message in replit itself, so you'll have to click on the Open In New Tab button found above the preview window, then open DevTools → Console
UE1 — no dependency array — will run after every render — initial App render, when counter is increased or decreased, and when console.log is clicked
useEffect(() => {console.log(`Count was updated: count is ${count} and you've clicked the console.log button ${clicks} time(s)`)})
UE2 — empty dependency array — will only run after the initial render of our App
useEffect(() => {console.log(`Count was updated: count is ${count} and you've clicked the console.log button ${clicks} time(s)`)},[])
UE3 — dependency array with count — will only run after count is changed via '-' or '+'. Nothing will happen when console.log is clicked
useEffect(() => {console.log(`Count was updated: count is ${count} and you've clicked the console.log button ${clicks} time(s)`)},[count])
As always, refer to the docs for more info:
Effect Hook
Feel free to reach out on any of my socials for questions, feedback, or just to connect / say hello 👋.
Top comments (2)
Can you give an example of the best practice for useEffect with fetch (or axios or any other API call)?
Another good simple explanation!