I have Function Component that utilizes hooks. In the useEffect hook, I simply want to fetch data from my back end and store the results in state. However, despite adding the data variable as a dependency, useEffect still fires on an infinite loop – even though the data hasn't changed. How can I stop useEffect from firing continuously?
I've tried the empty array hack, which DOES stop useEffect from continuously firing, but it's not the desired behavior. If the user saves new data, for example, useEffect should fire again to get updated data – I'm not looking to emulate componentDidMount.
const Invoices = () => {
const [invoiceData, setInvoiceData] = useState([]);
useEffect(() => {
const updateInvoiceData = async () => {
const results = await api.invoice.findData();
setInvoiceData(results);
};
updateInvoiceData();
}, [invoiceData]);
return (
<Table entries={invoiceData} />
);
};
I expected useEffect to fire after the initial render, and again ONLY when invoiceData changes.
Best Answer
The way the useEffect dependency array works is by checking for strict (===) equivalency between all of the items in the array from the previous render and the new render. Therefore, putting an array into your useEffect dependency array is extremely hairy because array comparison with === checks equivalency by reference not by contents.
Inside of your effect function, when you do
setInvoiceData(results)
you are updatinginvoiceData
to a new array. Even if all the items inside of that new array are exactly the same, the reference to the newinvoiceData
array has changed, causing the dependencies of the effect to differ, triggering the function again -- ad infinitum.One simple solution is to simply remove
invoiceData
from the dependency array. In this way, the useEffect function basically acts similar to componentDidMount in that it will trigger once and only once when the component first renders.This pattern is so common (and useful) that it is even mentioned in the official React Hooks API documentation: