关闭 React-Native 中的 useEffect 内存泄漏

Closing a useEffect Memory Leak in React-Native

在我的 expo 应用程序中从一组地图导航到另一组地图时,如果我在地图加载完成之前导航,我经常会遇到此错误:

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

如何堵住这个内存泄漏?

下面是我的代码:

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}
  ); 
});

原因: 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.

解决方案To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.

您的问题的解决方案:查看您的代码,当组件尝试卸载时您正在更改状态,您必须使用 useEffect hook,在setUserLatitudesetUserLongitudesetPositionError前加一个flag,确保组件没有从内存中卸载

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
 }
}, []);

了解更多信息 Clean up function

我同意瓦希德的回答, 顺便说一句,如果你的代码中已经有 react-navigation v5.x 会更容易, 很简单只用useFocusEffect方法,只在屏幕聚焦时执行的函数。我给出了使用 isActive 标志检查器添加第二次检查的示例。

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/