Javascript 在单击处理程序中使用两个useState设置程序时,只有一个有效
我用react中的挂钩制作了一个小型秒表组件。这是演示问题的最少代码 查看名为Javascript 在单击处理程序中使用两个useState设置程序时,只有一个有效,javascript,reactjs,react-hooks,jsx,use-state,Javascript,Reactjs,React Hooks,Jsx,Use State,我用react中的挂钩制作了一个小型秒表组件。这是演示问题的最少代码 查看名为resetTicks的函数。它有两个设置器setTicks和setTicking,只有setTicking起作用,即时钟暂停,有趣的是,如果我再次单击按钮,它才会重置时钟。我试着对两个setter重新排序调用,但都没有用 const StopWatch = () => { const [ticks,setTicks] = useState(0); const [ticking,setTicking] =
resetTicks
的函数。它有两个设置器setTicks
和setTicking
,只有setTicking
起作用,即时钟暂停,有趣的是,如果我再次单击按钮,它才会重置时钟。我试着对两个setter重新排序调用,但都没有用
const StopWatch = () => {
const [ticks,setTicks] = useState(0);
const [ticking,setTicking] = useState(false);
useEffect(() => {
setTimeout(() => {
if (ticking) setTicks(ticks + 1);
},10);
},[ticks,ticking]);
const toggleTicking = e => {
setTicking(!ticking);
}
const resetTicks = e => {
// these two setters are causing the issue
// only the setTicking is actually showing effect. I have tried switching
// their order but nothing works.
setTicking(false);
setTicks(0);
}
const min = Math.floor(ticks / 6000);
const sec = Math.floor((ticks - (min * 6000)) / 100);
const centis = ticks % 100;
return (
<WatchWrapper>
<WatchDisplay>
<span>{min < 10 ? '0': ''}{min}</span>
<span>:</span>
<span>{sec < 10 ? '0': ''}{sec}</span>
<span>:</span>
<span>{centis < 10 ? '0' : ''}{centis}</span>
</WatchDisplay>
<WatchControls>
<WatchBtn onClick={toggleTicking}>
{ticking ? 'stop' : 'play_arrow'}
</WatchBtn>
<WatchBtn onClick={resetTicks}>refresh</WatchBtn>
</WatchControls>
</WatchWrapper>
)
}
const秒表=()=>{
const[ticks,setTicks]=useState(0);
常数[ticking,setTicking]=使用状态(false);
useffect(()=>{
设置超时(()=>{
如果(滴答声)设置滴答声(滴答声+1);
},10);
},[滴答声,滴答声];
常量toggleTicking=e=>{
设置滴答声(!滴答声);
}
const resetTicks=e=>{
//这两个二传手造成了问题
//只有设置滴答声才真正起作用。我试过切换
//他们的命令无效。
设置滴答声(假);
滴答声(0);
}
最小常数=数学楼层(刻度/6000);
常数秒=数学楼层((刻度-(最小*6000))/100);
常数厘米=百分之一百;
返回(
{min<10?'0':''}{min}
:
{sec<10?'0':''}{sec}
:
{centis<10?'0':''}{centis}
{滴答声?'stop':'play_arrow'}
刷新
)
}
这是一个棘手的问题,您应该从控制台了解发生了什么。log
:
true
56
true
57
true
58
true
59
false
0
false
60
它确实被设置为0,但显然在某个点上,计划触发的旧setTimeout
,在其为60时,在旧的tick值上有一个闭包,因此它将其重置回原来的值
将超时时间增加到3秒,执行console.log(滴答声,滴答声)
在渲染中,您应该更清楚问题所在。这是一个棘手的问题,您应该从控制台了解发生了什么。log
:
true
56
true
57
true
58
true
59
false
0
false
60
useEffect(() => {
const interval = setInterval(() => {
if (ticking) setTicks(prevState => prevState + 1);
}, 10);
return () => clearInterval(interval);
}, [ticking]);
它确实被设置为0,但显然在某个点上,计划触发的旧setTimeout
,在其为60时,在旧的tick值上有一个闭包,因此它将其重置回原来的值
将超时时间增加到3秒,执行console.log(滴答声,滴答声)
在render中,您应该更清楚问题所在。这是因为setTicks
setter和setTimeout
内部回调的异步调用之间存在竞争条件。setTicks
setter更新ticks计数,但旧的ticks计数已存储在setTimeout
范围内。因此,setTimeout
会引发回调,并将ticks
的旧值作为参数提供给它。您需要在卸载组件时清除setTimeout
,以防止:
useEffect(() => {
const interval = setInterval(() => {
if (ticking) setTicks(prevState => prevState + 1);
}, 10);
return () => clearInterval(interval);
}, [ticking]);
useEffect(() => {
const timeout = setTimeout(() => {
if (ticking) setTicks(ticks + 1);
}, 10);
return () => clearTimeout(timeout);
}, [ticking, ticks]);
这是因为setTicks
setter和setTimeout
内部回调的异步调用之间存在竞争条件。setTicks
setter更新ticks计数,但旧的ticks计数已存储在setTimeout
范围内。因此,setTimeout
会引发回调,并将ticks
的旧值作为参数提供给它。您需要在卸载组件时清除setTimeout
,以防止:
useEffect(() => {
const timeout = setTimeout(() => {
if (ticking) setTicks(ticks + 1);
}, 10);
return () => clearTimeout(timeout);
}, [ticking, ticks]);
为了确保不存在竞争条件,您可以通过为计时器创建React引用,在resetTicks之前尝试重置setTimeout
const[ticks,setTicks]=React.useState(0);
常数[ticking,setTicking]=React.useState(false);
常量计时器=React.createRef();
React.useffect(()=>{
timer.current=setTimeout(()=>{
如果(滴答声)设置滴答声(滴答声+1);
},10);
},[滴答声,滴答声,计时器];
常量toggleTicking=e=>{
设置滴答声(!滴答声);
}
const resetTicks=e=>{
clearTimeout(定时器电流);
设置滴答声(假);
滴答声(0);
}
在此处使用Codesandbox进行测试:
为了确保不存在竞争条件,您可以通过创建计时器的React引用,在resetTicks之前尝试重置setTimeout
const[ticks,setTicks]=React.useState(0);
常数[ticking,setTicking]=React.useState(false);
常量计时器=React.createRef();
React.useffect(()=>{
timer.current=setTimeout(()=>{
如果(滴答声)设置滴答声(滴答声+1);
},10);
},[滴答声,滴答声,计时器];
常量toggleTicking=e=>{
设置滴答声(!滴答声);
}
const resetTicks=e=>{
clearTimeout(定时器电流);
设置滴答声(假);
滴答声(0);
}
在此处使用Codesandbox进行测试:
最初,您将记号的值定义为0,并在单击定义后将其定义为setTicks(0)
,因此不会重新渲染组件。尝试将其设置为0以外的其他值@DurgeshPal否。时钟运行时,我正在单击重置按钮,因此由于许多不同值的setTicks
调用,节拍已经更改。是。但我认为它在这两者之间设置了0值。最初,您将ticks的值定义为0,然后单击定义它setTicks(0)
,因此不会重新渲染组件。尝试将其设置为0以外的其他值@DurgeshPal否。时钟运行时,我正在单击重置按钮,因此由于许多不同值的setTicks
调用,节拍已经更改。是。但我认为,不知何故,它在两者之间设置了0值。你的眼光真敏锐。为什么我连这个都没想到。有趣的是,我在写“伙计,有一个场景,这个超时触发了预先安排好的时间,我会遇到问题”的时候,脑子里就有这个想法,但当实际问题发生时,我没有想到。哦,天哪。你的眼光真敏锐。为什么我连这个都没想到。有趣的是,当我写下“男人在那里”这句话的时候,我脑子里就有了它