You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Navigation:如何检测新屏幕已完全完成聚焦?

解决React Navigation中转场结束后再触发屏幕聚焦操作的问题

我太懂这个坑了!你提到的navigation.isFocused()和withNavigationFocus的isFocused属性,确实会在导航刚启动、转场动画还在运行的时候就返回true,这对相机、地图这类渲染开销极大的组件来说简直是灾难——提前加载不仅浪费资源,还可能导致转场卡顿甚至组件崩溃。

下面给你两个精准适配需求的解决方案,分别对应函数组件和类组件:

方案1:函数组件用useFocusEffect钩子

React Navigation专门提供了useFocusEffect,它会在屏幕完全完成转场、真正处于聚焦状态时执行回调,还自带清理机制,完美适配你的场景:

import { useFocusEffect } from '@react-navigation/native';
import React, { useState, useCallback } from 'react';
import { View, StyleSheet } from 'react-native';
import { Camera } from 'expo-camera';

export default function CameraScreen() {
  const [hasCameraPermission, setHasCameraPermission] = useState(null);
  const [cameraActive, setCameraActive] = useState(false);

  // 转场结束后激活相机,失去焦点时关闭
  useFocusEffect(
    useCallback(() => {
      setCameraActive(true);
      // 清理函数:屏幕失去焦点时触发
      return () => setCameraActive(false);
    }, [])
  );

  // 相机权限申请逻辑
  React.useEffect(() => {
    (async () => {
      const { status } = await Camera.requestCameraPermissionsAsync();
      setHasCameraPermission(status === 'granted');
    })();
  }, []);

  return (
    <View style={styles.container}>
      {/* 只有当屏幕真正聚焦且权限通过时,才渲染相机 */}
      {cameraActive && hasCameraPermission && (
        <Camera style={styles.camera} type={Camera.Constants.Type.back} />
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1 },
  camera: { flex: 1 },
});

这个钩子的核心优势是:只会在屏幕完全进入、转场动画结束后执行激活逻辑,返回的清理函数会在屏幕被推开(比如push新页面)或者返回上一页时自动触发,刚好用来销毁重组件,避免内存泄漏。

方案2:类组件用导航事件监听

如果你的项目还在使用类组件,可以通过监听focus和blur事件来实现同样的效果:

import React, { Component } from 'react';
import { View, StyleSheet } from 'react-native';
import { Camera } from 'expo-camera';

class MapScreen extends Component {
  state = {
    mapActive: false,
    hasCameraPermission: null,
  };

  componentDidMount() {
    // 监听屏幕聚焦事件(转场结束后触发)
    this.focusListener = this.props.navigation.addListener('focus', () => {
      this.setState({ mapActive: true });
    });
    // 监听屏幕失焦事件,用来清理组件
    this.blurListener = this.props.navigation.addListener('blur', () => {
      this.setState({ mapActive: false });
    });

    // 权限申请逻辑
    (async () => {
      const { status } = await Camera.requestCameraPermissionsAsync();
      this.setState({ hasCameraPermission: status === 'granted' });
    })();
  }

  componentWillUnmount() {
    // 移除监听,避免内存泄漏
    this.focusListener();
    this.blurListener();
  }

  render() {
    const { mapActive, hasCameraPermission } = this.state;
    return (
      <View style={styles.container}>
        {mapActive && hasCameraPermission && (
          <Camera style={styles.camera} type={Camera.Constants.Type.back} />
        )}
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: { flex: 1 },
  camera: { flex: 1 },
});

为什么之前的方法不好用?

navigation.isFocused()和withNavigationFocus的isFocused属性,判断的是组件是否已经挂载——当导航开始转场时,目标组件就已经开始挂载了,这时候isFocused会直接返回true,但转场动画还在进行,屏幕并没有真正处于“可用”状态。而上面的两种方案,都是监听屏幕真正完成聚焦的事件,完全避开了这个问题。

额外小提示

如果是自定义转场动画,或者需要更精确的时机,可以结合React Navigation的transitionEnd事件,但绝大多数场景下,focus事件和useFocusEffect已经足够满足需求啦。

内容的提问来源于stack exchange,提问作者blaz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 08:30:01