Learn how to prevent astronomical server costs by properly handling React data update in useEffectr
I am currently building out an app using Firebase and React and I was surprised to find out that I hit my 50k read limit for the day. I couldn’t imagine how in the hell that was possible since my app only runs locally my machine, and that I was the ONLY user.
When using useEffect
, your component will run code on render and executes the code inside of it when mounted. The problem that I was having was related to my app remounting when the state object was changed.
Here is an example of a poor way to perform an asynchronous call to the Firebase server.
function Example() {
const [vehicles, setVehicles] = useState([])
const vehiclesRef = collection(db, 'vehicles')
useEffect(() => {
const getVehicles = async () => {
const data = await getDocs(vehiclesRef)
setVehicles(data.docs.map( doc => (
{
...doc.data(),
id:doc.id
}
)
)
)
}
getVehicles()
}, [vehicles])
}
along with the error that I was getting.
Warning: Can’t perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
Whenever you call a setState variable, it will tell the component to re-render and run the code again. This means that setVehicles()
will continue to call which will cause and endless loop of re-rendering, which will cost you a lot money down the road.
Here is how I fixed it.
function Example() {
const [vehicles, setVehicles] = useState([])
const vehiclesRef = collection(db, 'vehicles')
const mounted = useRef(false)
useEffect(() => {
mounted.current = true
if(mounted.current){
const getVehicles = async () => {
const data = await getDocs(vehiclesRef)
if(isMounted){
setVehicles(data.docs.map( doc => (
{
...doc.data(),
id:doc.id
}
)
)
)
}
}
getVehicles()
}
return () => mounted.current = false
}, [vehicles])
}
Fortunately, the solution is quite straightforward when using a boolean flag. isMounted
will determine whether or not the setState code will run, which in this case is called setVehicles()
When is mounted is false
, the code will not run until that value is set to true
, when the component is mounted again.
Hope this clears things up! If you have React and programming topics you’d like for me to cover, email me at christopher.clemmons2020@gmail.com.
Want to start a project with me? Send a inquiry to hello@digyt.co or visit digyt.co/contact