为什么React中Stopwatch类实例调用start()后isRunning属性仍为false?
问题原因
- React函数组件重渲染导致实例重复创建
你直接在函数组件的顶层代码执行const stopwatch = new Stopwatch(),而函数组件每次状态变化触发重渲染时,都会从头到尾执行所有顶层代码,也就是说每重渲染一次就会生成一个全新的Stopwatch实例。你之前调用start()修改的是上一个实例的isRunning状态,后续调用stop()时用的是新生成的实例,新实例的isRunning默认值就是false,自然会触发"Stopwatch is already stopped"的报错,你观察到构造函数多次执行就是这个原因。 - 单实例场景下如果仍出现该问题,可检查是否在调用
stop()之前,有其他逻辑提前调用了reset()方法,reset会主动把isRunning重置为false。
解决方法
第一步:固定Stopwatch实例
使用React的useRef存储Stopwatch实例,保证组件整个生命周期内只会生成一个实例:
import React, { useRef, useEffect } from 'react'; import { Button, Row, Col } from 'antd'; import { I18n } from 'aws-amplify'; import Stopwatch from '../../../../../utils/Stopwatch'; export default function TenMeter(props) { // 用useRef存实例,仅初始化时生成一次 const stopwatchRef = useRef(new Stopwatch()); const startTrial = async (index) => { stopwatchRef.current.start(); } const completeTrial = async (index) => { stopwatchRef.current.stop(); resetParameters(); } const resetTrial = async (index) => { resetParameters(); } const resetTest = (index) => { resetParameters(); } const resetParameters = () => { stopwatchRef.current.reset(); } // 组件卸载时清除定时器,避免内存泄漏 useEffect(() => { return () => { stopwatchRef.current.reset(); } }, []) function getTrialsButtonGroup(index) { return ( <Row key={index} gutter={[16, 16]}> <Col xs={{ span: 24, offset: 0 }} lg={{ span: 24, offset: 0 }}> <span>{I18n.get('Trial')} {index + 1}</span> <Button style={{ marginLeft: "10px" }} onClick={() => startTrial(index)}>{I18n.get('Start')}</Button> <Button style={{ marginLeft: "10px" }} onClick={() => resetTrial(index)}>{I18n.get('Reset')}</Button> <Button style={{ marginLeft: "10px" }} onClick={() => completeTrial(index)}>{I18n.get('Stop')}</Button> </Col> </Row> ) }; return ( <div> {getTrialsButtonGroup(0)} </div> ); }
第二步:检查逻辑调用顺序
确认没有在start()之后、stop()之前主动调用reset()的逻辑,避免isRunning被提前重置。
内容的提问来源于stack exchange,提问作者kataba
相关产品推荐
相关产品推荐

