React Native 提示useEffect未清理订阅与异步任务问题求助
问题1:代码存在的问题
你遇到的报错是由以下几个未正确处理的清理逻辑导致的:
- 导航监听器依赖错误:你在
beforeRemove监听器中使用的是isDoneRef.current(ref值),但useEffect依赖数组中传入的是isDone(状态值)。如果你仅使用ref做判断,不需要将状态加入依赖数组,否则isDone每次变更都会重复销毁、重建监听器,容易出现边界case下的监听泄漏。 - 单次定位请求未取消:
getLocation中调用的Geolocation.getCurrentPosition是异步请求,你没有对应的取消逻辑。如果组件在请求返回前卸载,回调中的setStartPosition会触发已卸载组件的状态更新,直接导致报错。 - 异步定位监听的竞态问题:
getLocationUpdates是异步函数,内部存在await hasLocationPermission、await startForegroundService两个等待逻辑。如果组件在等待过程中卸载,清理函数会先执行,此时currentPositionRef还未被赋值,clearWatch逻辑不会触发。等待结束后才会执行Geolocation.watchPosition赋值给currentPositionRef,这个监听就永远不会被清理,后续回调触发setCurrentPosition就会报错。 - 卸载后执行状态更新:第三个定时器useEffect的清理函数中写了
setTimer(0),组件卸载时清理函数执行,此时调用setTimer会直接触发已卸载组件的状态更新,属于典型的泄漏报错场景。
问题2:精准定位未清理函数的方法
- 给每个useEffect的回调、清理函数加唯一标识的
console.log,同时给所有异步回调(比如定位的成功/失败回调、定时器回调)加上标识日志,报错前最后打印的日志对应的函数就是泄漏的来源。 - 新增一个
isMountedRef引用:在组件顶层声明const isMountedRef = useRef(false),在所有useEffect的回调开头将isMountedRef.current = true,清理函数中将isMountedRef.current = false。在所有setState调用前先判断isMountedRef.current是否为true,若为false则打印当前回调的标识,即可直接定位到泄漏的异步逻辑。 - 开启React严格模式(StrictMode),此时开发环境下组件会执行「挂载-卸载-重新挂载」的流程,会放大泄漏问题,更容易快速复现定位。
- 可以在每个
setState调用前打堆栈日志:console.trace('触发状态更新'),就能直接看到是哪个函数触发了卸载后的状态更新。
内容的提问来源于stack exchange,提问作者Bagus Amrullah Fikri
相关产品推荐
相关产品推荐

