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;
问题分析与修复方案
原代码的核心问题
- useEffect无依赖数组:组件每次重渲染都会重复创建定时器,导致多个定时器同时运行,引发内存泄漏
- setInterval参数错误:时间间隔
20被错误传递给setUpdateFunction,而非setInterval的第二个参数,定时器默认以0ms间隔疯狂执行 - 用useState存定时器ID不合理:useState更新是异步的,清理定时器时可能拿到旧ID,无法正确清除
- 传感器订阅未绑定生命周期:未在组件挂载时自动订阅,卸载时未自动取消,易造成内存泄漏
修复后的完整代码
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;
关键修复点说明
- 添加空依赖数组:确保初始化逻辑仅在组件挂载时执行一次,避免重复创建定时器和订阅
- 修正setInterval参数:将20ms间隔正确传递给
setInterval - 直接存储定时器ID:在useEffect内部用变量存定时器ID,避免异步更新导致的清理失败
- 传感器订阅与生命周期绑定:在同一useEffect内完成订阅和取消,保证资源及时释放
- 同步传感器更新间隔:让传感器更新频率与定时器一致,确保每次计算都能拿到最新数据
- 优化数据显示:用
toFixed(4)限制小数位数,避免UI显示过长数值
内容的提问来源于stack exchange,提问作者Jamzy01
相关产品推荐
相关产品推荐

