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

React Native页面聚焦时如何持续运行函数处理传感器数据

问题:结合陀螺仪与加速度计数据的定时更新函数失效

我正在用React Native的expo-sensors检测手机陀螺仪(Gyroscope)和加速度计(Accelerometer)数据,需要每20ms执行一次循环,将两者数据相乘后更新设备朝向,但之前写的定时函数无法正常工作。

失败的定时函数代码

const [updateFunction, setUpdateFunction] = useState(0);

useEffect(() => {
  setUpdateFunction(setInterval(() => {
    console.log("Update")
  }), 20);

  return () => {
    clearInterval(updateFunction);
  }
})

完整代码

import { View, Text, TouchableOpacity, Alert } from 'react-native';

import AppStyles from '../styles/styles'

import { Gyroscope, Accelerometer } from 'expo-sensors';

import { useState, useEffect } from 'react';

import * as React from 'react';

function Home(props) {
    // Gyroscope Data
    const [gyroData, setGyroData] = useState({
      x: 0,
      y: 0,
      z: 0
    });

    // Accelerometer Data
    const [accelData, setAccelData] = useState({
      x: 0,
      y: 0,
      z: 0
    });

    const [accelSpeed, setAccelSpeed] = useState(0);

    // Device Orientation
    const [deviceOrientation, setDeviceOrientation] = useState({
      x: 0,
      y: 0,
      z: 0
    });

    // Gyro Data
    const [gyroSubscription, setGyroSubscription] = useState(null); // Gyroscope
    const [accelSubscription, setAccelSubscription] = useState(null); // Accelerometer

    Gyroscope.setUpdateInterval(1000);
    Accelerometer.setUpdateInterval(100);

    // Gyroscope
    const _gyrosubscribe = () => {
      setGyroSubscription(
        Gyroscope.addListener(gyroscopeData => {
          setGyroData(gyroscopeData);
        })
      );
    };
  
    const _gyrounsubscribe = () => {
      gyroSubscription && gyroSubscription.remove();
      setGyroSubscription(null);
    };

    const _accelsubscribe = () => {
      setAccelSubscription(
        Accelerometer.addListener(accelerometerData => {
          setAccelData(accelerometerData);
          setAccelSpeed(Math.floor(Math.abs(1 - Math.cbrt(Math.pow(accelerometerData.x, 2) + Math.pow(accelerometerData.y, 2) + Math.pow(accelerometerData.z, 2))) * 100));
        })
      );
    };

    const _accelunsubscribe = () => {
      accelSubscription && accelSubscription.remove();
      setAccelSubscription(null);
    };

    // Device Orientation Update Function
    const [updateFunction, setUpdateFunction] = useState(0);

    useEffect(() => {
      setUpdateFunction(setInterval(() => {
        console.log("Update")
      }), 20);

      return () => {
        clearInterval(updateFunction);
      }
    })

    // Page Navigation
    const navigation = props.navigation;

    return (
        <View style={{ flex: 1, alignItems: 'center', justifyContent: 'space-around', marginTop: 50 }}>
          <Text style={AppStyles.homescreentitle}>Phone Soundboard</Text>
          <View style={{height: "70%", width: "100%", alignItems: "center"}}>
            <TouchableOpacity style={AppStyles.homebuttontouchableopacity} onPress={() => {
              navigation.navigate("mouse");
            }}>
                <Text style={AppStyles.homebuttontext}>
                  Mouse
                </Text>
            </TouchableOpacity>
            <TouchableOpacity style={AppStyles.homebuttontouchableopacity} onPress={() => {
              navigation.navigate("test");
            }}>
                <Text style={AppStyles.homebuttontext}>
                  Test Section
                </Text>
            </TouchableOpacity>
            <TouchableOpacity style={AppStyles.homebuttontouchableopacity} onPress={() => {
              
            }}>
                <Text style={AppStyles.homebuttontext}>
                  Check for Dev Update Func
                </Text>
            </TouchableOpacity>
            <View>
              <Text>
                Device Orientation Data: x: {deviceOrientation.x} y: {deviceOrientation.y} z: {deviceOrientation.z}
              </Text>
              <Text>
                Gyroscope Data: x: {gyroData.x} y: {gyroData.y} z: {gyroData.z}
              </Text>
              <Text>
                Accelerometer Data: {
                  accelSpeed
                }
              </Text>
            </View>
          </View>
        </View>
      );
}

export default Home;

问题分析与修复方案

原代码的核心问题

  1. useEffect无依赖数组:组件每次重渲染都会重复创建定时器,导致多个定时器同时运行,引发内存泄漏
  2. setInterval参数错误:时间间隔20被错误传递给setUpdateFunction,而非setInterval的第二个参数,定时器默认以0ms间隔疯狂执行
  3. 用useState存定时器ID不合理:useState更新是异步的,清理定时器时可能拿到旧ID,无法正确清除
  4. 传感器订阅未绑定生命周期:未在组件挂载时自动订阅,卸载时未自动取消,易造成内存泄漏

修复后的完整代码

import { View, Text, TouchableOpacity, Alert } from 'react-native';
import AppStyles from '../styles/styles'
import { Gyroscope, Accelerometer } from 'expo-sensors';
import { useState, useEffect } from 'react';
import * as React from 'react';

function Home(props) {
    const [gyroData, setGyroData] = useState({ x: 0, y: 0, z: 0 });
    const [accelData, setAccelData] = useState({ x: 0, y: 0, z: 0 });
    const [accelSpeed, setAccelSpeed] = useState(0);
    const [deviceOrientation, setDeviceOrientation] = useState({ x: 0, y: 0, z: 0 });

    const navigation = props.navigation;

    useEffect(() => {
        // 设置传感器更新间隔与定时器同步,确保数据时效性
        Gyroscope.setUpdateInterval(20);
        Accelerometer.setUpdateInterval(20);

        // 订阅陀螺仪数据
        const gyroSubscription = Gyroscope.addListener(gyroscopeData => {
            setGyroData(gyroscopeData);
        });

        // 订阅加速度计数据
        const accelSubscription = Accelerometer.addListener(accelerometerData => {
            setAccelData(accelerometerData);
            setAccelSpeed(Math.floor(Math.abs(1 - Math.cbrt(Math.pow(accelerometerData.x, 2) + Math.pow(accelerometerData.y, 2) + Math.pow(accelerometerData.z, 2))) * 100));
        });

        // 定时计算并更新设备朝向(每20ms一次)
        const timer = setInterval(() => {
            // 将陀螺仪与加速度计对应轴数据相乘
            const newOrientation = {
                x: gyroData.x * accelData.x,
                y: gyroData.y * accelData.y,
                z: gyroData.z * accelData.z
            };
            setDeviceOrientation(newOrientation);
        }, 20);

        // 组件卸载时清理所有资源
        return () => {
            gyroSubscription.remove();
            accelSubscription.remove();
            clearInterval(timer);
        };
    }, []); // 空依赖数组确保仅在组件挂载时执行一次

    return (
        <View style={{ flex: 1, alignItems: 'center', justifyContent: 'space-around', marginTop: 50 }}>
            <Text style={AppStyles.homescreentitle}>Phone Soundboard</Text>
            <View style={{height: "70%", width: "100%", alignItems: "center"}}>
                <TouchableOpacity style={AppStyles.homebuttontouchableopacity} onPress={() => {
                    navigation.navigate("mouse");
                }}>
                    <Text style={AppStyles.homebuttontext}>Mouse</Text>
                </TouchableOpacity>
                <TouchableOpacity style={AppStyles.homebuttontouchableopacity} onPress={() => {
                    navigation.navigate("test");
                }}>
                    <Text style={AppStyles.homebuttontext}>Test Section</Text>
                </TouchableOpacity>
                <TouchableOpacity style={AppStyles.homebuttontouchableopacity} onPress={() => {
                    console.log("Current orientation:", deviceOrientation);
                }}>
                    <Text style={AppStyles.homebuttontext}>Check for Dev Update Func</Text>
                </TouchableOpacity>
                <View>
                    <Text>
                        Device Orientation Data: x: {deviceOrientation.x.toFixed(4)} y: {deviceOrientation.y.toFixed(4)} z: {deviceOrientation.z.toFixed(4)}
                    </Text>
                    <Text>
                        Gyroscope Data: x: {gyroData.x.toFixed(4)} y: {gyroData.y.toFixed(4)} z: {gyroData.z.toFixed(4)}
                    </Text>
                    <Text>
                        Accelerometer Data: {accelSpeed}
                    </Text>
                </View>
            </View>
        </View>
    );
}

export default Home;

关键修复点说明

  1. 添加空依赖数组:确保初始化逻辑仅在组件挂载时执行一次,避免重复创建定时器和订阅
  2. 修正setInterval参数:将20ms间隔正确传递给setInterval
  3. 直接存储定时器ID:在useEffect内部用变量存定时器ID,避免异步更新导致的清理失败
  4. 传感器订阅与生命周期绑定:在同一useEffect内完成订阅和取消,保证资源及时释放
  5. 同步传感器更新间隔:让传感器更新频率与定时器一致,确保每次计算都能拿到最新数据
  6. 优化数据显示:用toFixed(4)限制小数位数,避免UI显示过长数值

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 19:09:24