clearInterval 不会在 React.useEffect 钩子中被调用
clearInterval does not get called inside React.useEffect hook
我的 React 游戏有一个 <Clock/>
组件来跟踪时间。
游戏暂停时计时器应该停止。
我正在使用 Redux 来管理 play/pause 状态,以及经过的时间。
const initialState = { inProgress: false, timeElapsed: 0 }
inProgress
状态由另一个组件上的按钮处理,该按钮调度更新商店的操作(仅针对 inProgress
值)。
<Clock/>
组件在其 useEffect
挂钩中递增 timeElapsed
setInterval
。还是不清楚。
import React from 'react';
import { connect } from 'react-redux';
const Clock = ({ dispatch, inProgress, ticksElapsed }) => {
React.useEffect(() => {
const progressTimer = setInterval(function(){
inProgress ? dispatch({ type: "CLOCK_RUN" }) : clearInterval(progressTimer);
}, 1000)
}, [inProgress]);
return (
<></>
)
};
let mapStateToProps = ( state ) => {
let { inProgress, ticksElapsed } = state.gameState;
return { inProgress, ticksElapsed };
}
export default connect(
mapStateToProps,
null,
)(Clock);
在 setInterval
中,当 inProgress
为 false
时,我希望 clearInterval(progressTimer)
停止计时。
此外,还有另一个问题,即在 useEffect
挂钩中遗漏 [inProgress]
会导致计时器以荒谬的速度增加,从而导致应用程序崩溃。
谢谢。
对于传递给 setInterval 的函数,inProgress
是一个 stale closure。
在cleanup函数中清除interval即可解决:
const Clock = ({ dispatch, inProgress, ticksElapsed }) => {
React.useEffect(() => {
const progressTimer = setInterval(function () {
inProgress && dispatch({ type: 'CLOCK_RUN' });
}, 500);
return () =>
//inprogress is stale so when it WAS true
// it must now be false for the cleanup to
// be called
inProgress && clearInterval(progressTimer);
}, [dispatch, inProgress]);
return <h1>{ticksElapsed}</h1>;
};
const App = () => {
const [inProgress, setInProgress] = React.useState(false);
const [ticksElapsed, setTicksElapsed] = React.useState(0);
const dispatch = React.useCallback(
() => setTicksElapsed((t) => t + 1),
[]
);
return (
<div>
<button onClick={() => setInProgress((p) => !p)}>
{inProgress ? 'stop' : 'start'}
</button>
<Clock
inProgress={inProgress}
dispatch={dispatch}
ticksElapsed={ticksElapsed}
/>
</div>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
我的 React 游戏有一个 <Clock/>
组件来跟踪时间。
游戏暂停时计时器应该停止。
我正在使用 Redux 来管理 play/pause 状态,以及经过的时间。
const initialState = { inProgress: false, timeElapsed: 0 }
inProgress
状态由另一个组件上的按钮处理,该按钮调度更新商店的操作(仅针对 inProgress
值)。
<Clock/>
组件在其 useEffect
挂钩中递增 timeElapsed
setInterval
。还是不清楚。
import React from 'react';
import { connect } from 'react-redux';
const Clock = ({ dispatch, inProgress, ticksElapsed }) => {
React.useEffect(() => {
const progressTimer = setInterval(function(){
inProgress ? dispatch({ type: "CLOCK_RUN" }) : clearInterval(progressTimer);
}, 1000)
}, [inProgress]);
return (
<></>
)
};
let mapStateToProps = ( state ) => {
let { inProgress, ticksElapsed } = state.gameState;
return { inProgress, ticksElapsed };
}
export default connect(
mapStateToProps,
null,
)(Clock);
在 setInterval
中,当 inProgress
为 false
时,我希望 clearInterval(progressTimer)
停止计时。
此外,还有另一个问题,即在 useEffect
挂钩中遗漏 [inProgress]
会导致计时器以荒谬的速度增加,从而导致应用程序崩溃。
谢谢。
对于传递给 setInterval 的函数,inProgress
是一个 stale closure。
在cleanup函数中清除interval即可解决:
const Clock = ({ dispatch, inProgress, ticksElapsed }) => {
React.useEffect(() => {
const progressTimer = setInterval(function () {
inProgress && dispatch({ type: 'CLOCK_RUN' });
}, 500);
return () =>
//inprogress is stale so when it WAS true
// it must now be false for the cleanup to
// be called
inProgress && clearInterval(progressTimer);
}, [dispatch, inProgress]);
return <h1>{ticksElapsed}</h1>;
};
const App = () => {
const [inProgress, setInProgress] = React.useState(false);
const [ticksElapsed, setTicksElapsed] = React.useState(0);
const dispatch = React.useCallback(
() => setTicksElapsed((t) => t + 1),
[]
);
return (
<div>
<button onClick={() => setInProgress((p) => !p)}>
{inProgress ? 'stop' : 'start'}
</button>
<Clock
inProgress={inProgress}
dispatch={dispatch}
ticksElapsed={ticksElapsed}
/>
</div>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>