Reputation: 966
When navigating from one set of maps in my expo app to another, I often get this error if I navigate before the maps are done loading:
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 %s.%s, a useEffect cleanup function
How do I plug this memory leak?
Below is my code:
function MapsScreen({navigation}) {
const [user_latitude, setUserLatitude] = useState(0)
const [user_longitude, setUserLongitude] = useState(0)
const [position_error, setPositionError] = useState(null)
useEffect(() => {
navigator.geolocation.getCurrentPosition(position => {
setUserLatitude(position.coords.latitude);
setUserLongitude(position.coords.longitude);
setPositionError(null);
},
error => setPositionError(error.message),
{enableHighAccuracy: true, timeout: 20000, maximumAge: 2000}
);
});
Upvotes: 1
Views: 2409
Reputation: 3187
Reason:
You will get this warning when your component is unmounted from the memory and some async task is in the process like you make a network call and tries to leave the screen before resolving the promise.
Solution: To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
A solution to your problem: As looking at your code you are changing your state when the component tries to unmount, You have to use the cleanup function with the useEffect
hook, add a flag before setUserLatitude
, setUserLongitude
, setPositionError
to make sure that the component is not unmounted from the memory
function MapsScreen({navigation}) {
const [user_latitude, setUserLatitude] = useState(0)
const [user_longitude, setUserLongitude] = useState(0)
const [position_error, setPositionError] = useState(null)
useEffect(() => {
let mounted = true // Add this
navigator.geolocation.getCurrentPosition(position => {
if(mounted){ // Add this
setUserLatitude(position.coords.latitude);
setUserLongitude(position.coords.longitude);
setPositionError(null);
}
},
error => setPositionError(error.message),
{enableHighAccuracy: true, timeout: 20000, maximumAge: 2000}
);
return () => {
mounted = false // add this
}
}, []);
Read more about it Clean up function
Upvotes: 1
Reputation: 67
I agree with Waheed answer,
btw it is more easier if you already have react-navigation v5.x in your code ,
it's simple just use useFocusEffect
method, the function executed when screen is focused only. I give example with the addition of second check using isActive
flag checker.
import React, { useEffect, useState } from 'react'
import { useFocusEffect } from '@react-navigation/native'
function MapsScreen({ navigation }) {
const [user_latitude, setUserLatitude] = useState(0)
const [user_longitude, setUserLongitude] = useState(0)
const [position_error, setPositionError] = useState(null)
useFocusEffect(
React.useCallback(() => {
let isActive = true
const fetchGeoPositon = () => {
navigator.geolocation.getCurrentPosition(
position => {
if (isActive) {
setUserLatitude(position.coords.latitude)
setUserLongitude(position.coords.longitude)
setPositionError(null)
}
},
error => isActive && setPositionError(error.message),
{ enableHighAccuracy: true, timeout: 20000, maximumAge: 2000 }
)
}
fetchGeoPositon()
return () => {
isActive = false
}
}, [])
)
}
https://reactnavigation.org/docs/use-focus-effect/
Upvotes: 0