Last updated: Apr 6, 2024
Reading timeยท3 min
Use the useEffect
hook to only call a function once in React. When the
useEffect
hook is passed an empty dependencies array, it is only run when the
component mounts.
This is the preferred approach when you have to fetch data when the component mounts.
import {useEffect, useState} from 'react'; const App = () => { const [num, setNum] = useState(0); useEffect(() => { // ๐๏ธ Only runs once console.log('useEffect ran'); function incrementNum() { setNum(prev => prev + 1); } incrementNum(); }, []); // ๐๏ธ Empty dependencies array return ( <div> <h2>Number is {num}</h2> </div> ); }; export default App;
The incrementNum
function is only called once - when the component mounts.
The second parameter we passed to the useEffect hook is an array of dependencies.
We specified an empty array of dependencies, so the function we passed to the
useEffect
hook will only be called once.
Notice that we defined the incrementNum
function inside of the function we
passed to the useEffect
hook.
Alternatively, we could have defined the function outside of the component or memoized it.
Here is an example of how you would call a function to fetch data only once - when the component mounts.
import {useEffect, useState} from 'react'; const App = () => { const [data, setData] = useState({data: []}); const [err, setErr] = useState(''); useEffect(() => { // ๐๏ธ This only runs once console.log('useEffect ran'); // ๐๏ธ fetch data from remote API async function getUsers() { try { const response = await fetch('https://reqres.in/api/users', { method: 'GET', headers: { Accept: 'application/json', }, }); if (!response.ok) { throw new Error(`Error! status: ${response.status}`); } const result = await response.json(); console.log('result is: ', JSON.stringify(result, null, 4)); setData(result); } catch (err) { setErr(err.message); } } getUsers(); }, []); // ๐๏ธ Empty dependencies array console.log(data); return ( <div> {err && <h2>{err}</h2>} {data.data.map(person => { return ( <div key={person.id}> <h2>{person.email}</h2> <h2>{person.first_name}</h2> <h2>{person.last_name}</h2> <br /> </div> ); })} </div> ); }; export default App;
We defined a getUsers
function inside of the useEffect
hook. The function is
called only once - when the component mounts and makes a single request to a
remote API to fetch some data.
useEffect
hook is run only once because we passed it an empty dependencies array as the second parameter.If you need to fetch data on button click, check out the following article.
An alternative to defining the function you want to only call once inside of the
useEffect
hook is to define the function outside of the component.
useCallback
hook to memoize the functionAlternatively, you can use the useCallback
hook to memoize the function and
pass it to the dependencies array of useEffect
.
import {useCallback, useEffect, useState} from 'react'; const App = () => { const [num, setNum] = useState(0); // ๐๏ธ Memoize function (doesn't get re-created every render) const incrementNum = useCallback(() => { setNum(prev => prev + 1); }, []); useEffect(() => { // ๐๏ธ This only runs once incrementNum(); // ๐๏ธ Include it in the dependencies array }, [incrementNum]); return ( <div> <h2>Number is {num}</h2> </div> ); }; export default App;
The useCallback
hook takes an inline callback function and a dependencies
array and returns a memoized version of the callback that only changes if one of
the dependencies has changed.
Now that the incrementNum
function is stable and doesn't change between
renders, we can safely add it to the dependencies of the useEffect
hook and it
still runs only once.