React’s useEffect and arrays in its’ dependency array
Could I get any more arrays into that title? I’m working on a React app at the moment. Adopting hooks has made my life a bit easier. But useEffect‘s dependency array took a while to get my head around – specifically what to do if your only dependency is itself an array.
If useEffect‘s dependency array itself contains an array, like this, you get an infinite loop and effectively perform a DOS attack on your own API.
const Thing = () => {
const [arrayOfThings, setArrayOfThings] = useState([]);
useEffect(() => {
// Imagine some code here to
// fetch things from an API
setArrayOfThings(loadedArrayOfThings);
}, [arrayOfThings])
return (
<ul>
{arrayOfThings.map(el => (<li>{el.title}</li>))}
</ul>
)
}
I imagine it’s because useEffect isn’t doing a deep compare on the dependencies. So, perhaps changing it so useEffect is dependent on the length of the array will help?
useEffect(() => {
// Imagine some code here to
// fetch things from an API
setArrayOfThings(loadedArrayOfThings);
}, [arrayOfThings.length])
Well, it does, but it still requests the data from the API twice – once on first render, and again after the first fetch because the length of the array changes. The fix I found is to use a second useState variable:
const Thing = () => {
const [arrayOfThings, setArrayOfThings] = useState([]);
const [doFetchData, setDoFetchData] = useState(true);
useEffect(() => {
if (doFetchData) {
// Imagine some code here to
// fetch things from an API
setArrayOfThings(loadedArrayOfThings);
setDoFetchData(false)
}
}, [doFetchData])
return (
<ul>
{arrayOfThings.map(el => (<li>{el.title}</li>))}
</ul>
)
}
The ensures the data is only loaded once. Additionally, it gives us a way to precisely control when to go and re-fetch the data (using setDoFetchData(true)). Better.

