MK_Pierce
MK_Pierce

Reputation: 966

Closing a useEffect Memory Leak in React-Native

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

Answers (2)

Waheed Akhtar
Waheed Akhtar

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

Handi
Handi

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

Related Questions