React Native Expo中unstable_pressDelay未完全生效问题排查
问题分析
你的反应时间测试界面存在以下核心问题:
- 用全局变量
c控制界面颜色状态,和React的count状态不同步,导致流程逻辑混乱。 - 没有状态锁机制,用户在测试流程中重复按压会触发新的
start函数,跳过原有流程。 - 固定1秒延迟而非随机时长,且未清理定时器,多个定时器叠加导致状态异常。
unstable_pressDelay无法阻止重复触发pressIn,该参数仅用于延迟Pressable的"press"状态激活,不是防止事件重复调用。
解决方案
重构代码逻辑,使用React状态管理流程阶段,添加状态锁防止重复触发,同时修复定时器和时间计算逻辑:
import * as React from 'react'; import { Pressable, Text, View } from 'react-native'; import { MaterialCommunityIcons } from "@expo/vector-icons"; import { SafeAreaProvider } from 'react-native-safe-area-context'; import styles from './Styles'; import { entireScreenWidth } from '../App'; // 定义流程阶段:0=初始(青色),1=等待(红色),2=测试(绿色) const PHASES = { INIT: 0, WAITING: 1, TESTING: 2 }; const colours = ['cyan', 'red', 'lime']; export default function ReactionScreen({navigation}) { const [currentPhase, setCurrentPhase] = React.useState(PHASES.INIT); const [reactionTime, setReactionTime] = React.useState(null); const [timerId, setTimerId] = React.useState(null); const startTimeRef = React.useRef(null); const maxDelay = 5000; const minDelay = 500; // 清理定时器(组件卸载或流程重置时) React.useEffect(() => { return () => { if (timerId) clearTimeout(timerId); }; }, [timerId]); function startTest() { // 只有初始状态才能启动测试 if (currentPhase !== PHASES.INIT) return; // 进入等待阶段(红色) setCurrentPhase(PHASES.WAITING); setReactionTime(null); // 生成随机延迟 const randomDelay = Math.random() * (maxDelay - minDelay) + minDelay; const id = setTimeout(() => { // 进入测试阶段(绿色),记录开始时间 setCurrentPhase(PHASES.TESTING); startTimeRef.current = performance.now(); }, randomDelay); setTimerId(id); } function endTest() { // 只有测试阶段松开才计算反应时间 if (currentPhase !== PHASES.TESTING) { // 如果在等待阶段松开,直接重置流程 resetTest(); return; } // 计算反应时间 const finishTime = performance.now(); const time = Math.round(finishTime - startTimeRef.current); setReactionTime(time); // 重置为初始状态 resetTest(); } function resetTest() { if (timerId) clearTimeout(timerId); setTimerId(null); setCurrentPhase(PHASES.INIT); } return ( <SafeAreaProvider style={{ backgroundColor: colours[currentPhase], flex: 1 }}> <Pressable onPressIn={startTest} onPressOut={endTest} style={{ flex: 1, backgroundColor: 'transparent' }} android_ripple={{ color: 'blue', borderless: false, foreground: true }} > <MaterialCommunityIcons name="lightning-bolt" size={0.25 * entireScreenWidth} color="#7F00FF" style={[styles.buttonIcon, { textAlign: 'center', bottom: '-30%' }]} /> <Text style={[styles.buttonLabel(0.1), { textAlign: 'center', bottom: '-60%' }]}> {currentPhase === PHASES.INIT ? 'Press to Start' : currentPhase === PHASES.WAITING ? 'Wait for Green' : 'Release Now!'} </Text> {reactionTime && ( <Text style={{ textAlign: 'center', marginTop: 20, fontSize: 24, fontWeight: 'bold' }}> Your reaction time: {reactionTime}ms </Text> )} </Pressable> </SafeAreaProvider> ); }
关键改动说明
- 使用
currentPhase状态管理流程阶段,替代全局变量c,确保React状态同步。 - 通过
currentPhase判断流程状态,只有初始状态允许触发startTest,避免重复启动流程。 - 用
useRef存储startTime,避免状态更新导致的异步问题。 - 每次启动测试时生成随机延迟,并在重置流程或组件卸载时清理定时器,防止内存泄漏和状态混乱。
- 只有在绿色测试阶段松开时才计算反应时间,等待阶段松开直接重置流程,符合测试逻辑。
- 更新提示文本,根据当前阶段显示对应指引,提升用户体验。
内容的提问来源于stack exchange,提问作者Arjun
相关产品推荐
相关产品推荐

