set状态不会立即在设置内部更新状态

我正在尝试构建一个模拟时钟,秒针每秒旋转一次,最小指针每分钟旋转6度,小时指针每12分钟旋转6度。


这是代码和框:https://codesandbox.io/s/react-example-d7mes?file=/Clock.js


每当最小指针的角度为其中任何一个(12分钟度)时,我都会将时针旋转6度一次。[72, 144, 216, 288, 360]


这就是我正在做的事情:


 let twelveMinDegrees = [72, 144, 216, 288, 360];


 setInterval(() => {

        this.setState(prev => ({

            sec: prev.sec == 360 ? 6 : prev.sec + 6,  //degrees

            min: prev.sec == 354 ? (prev.min == 360 ? 6 : prev.min + 6) : prev.min,  //degrees

            hrs: (function(){  //degrees


                const indx = twelveMinDegrees.findIndex(el => el == prev.min)

                if(!minChanged && indx >=0){ //only change once for every 12min

                    minChanged = true; 

                    let incHrs = prev.hrs + (6*indx);

                    console.log(incHrs);

                    return incHrs; 

                }else{

                    if(!twelveMinDegrees.includes(prev.min)){

                        minChanged = false;

                    }

                    return prev.hrs;

                }

            })()

         }))

    }, 1000)

但是时针不会改变,并且第二次被设置回other部分中的上一个值,并且返回的值被忽略,因为在状态更新之前,称为下一秒,它返回的仍然是旧值(不是在incHrselseprev.hrsif(!minChanged && indx >=0))


如何解决此问题?


FFIVE
浏览 198回答 1
1回答

沧海一幻觉

这里有一个基本的设计问题,那就是设置间隔是保持时间的错误工具。 只保证回调不会运行至少1000毫秒,而不是保证它将在1000毫秒内完全运行,所以你最终会得到漂移和跳过的时间。setInterval我建议使用请求动画帧和日期库或性能来确定何时发生了刻度。通过此设置,您可以使用以下命令将时针与小时剩余的分钟数成比例地缩放:(hours&nbsp;+&nbsp;minutes&nbsp;/&nbsp;60)&nbsp;*&nbsp;30&nbsp;+&nbsp;180如果您希望对时针调整进行更粗糙的粒度,请将分钟截断为 6 个不同的块:(hours&nbsp;+&nbsp;floor(minutes&nbsp;/&nbsp;10)&nbsp;*&nbsp;10&nbsp;/&nbsp;60)&nbsp;*&nbsp;30&nbsp;+&nbsp;180在数学上执行此操作比在硬编码数组中查找增量点要混乱得多。下面是一个最小的示例,可用于保持准确的时间(我将样式留给您):.hand {&nbsp; width: 2px;&nbsp; height: 40%;&nbsp; background-color: black;&nbsp; transform-origin: top center;&nbsp; position: absolute;&nbsp; border-radius: 3px;&nbsp; top: 50%;&nbsp; left: 50%;}.analog-clock {&nbsp; position: relative;&nbsp; border-radius: 50%;&nbsp; border: 1px solid #aaa;&nbsp; height: 120px;&nbsp; width: 120px;}<script type="text/babel" defer>const {Fragment, useEffect, useState, useRef} = React;const Clock = () => {&nbsp; const [date, setDate] = useState(new Date());&nbsp; const requestRef = useRef();&nbsp; let prevDate = null;&nbsp;&nbsp;&nbsp; const tick = () => {&nbsp; &nbsp; const now = new Date();&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; if (prevDate && now.getSeconds() !== prevDate.getSeconds()) {&nbsp; &nbsp; &nbsp; setDate(now);&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; prevDate = now;&nbsp; &nbsp; requestRef.current = requestAnimationFrame(tick);&nbsp; };&nbsp;&nbsp;&nbsp; useEffect(() => {&nbsp; &nbsp; requestRef.current = requestAnimationFrame(tick);&nbsp; &nbsp; return () => cancelAnimationFrame(requestRef.current);&nbsp; }, []);&nbsp;&nbsp;&nbsp; const pad = n => n.toString().padStart(2, 0);&nbsp;&nbsp;&nbsp; const computeHourDeg = date =>&nbsp;&nbsp; &nbsp; (date.getHours() + ~~(date.getMinutes() / 10) * 10 / 60) * 30 + 180&nbsp; ;&nbsp; return (&nbsp; &nbsp; <Fragment>&nbsp; &nbsp; &nbsp; <div className="analog-clock">&nbsp; &nbsp; &nbsp; &nbsp; <div&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; className="hand"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; style={{transform: `rotate(${6 * date.getSeconds() + 180}deg)`}}&nbsp; &nbsp; &nbsp; &nbsp; ></div>&nbsp; &nbsp; &nbsp; &nbsp; <div&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; className="hand"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; style={{transform: `rotate(${6 * date.getMinutes() + 180}deg)`}}&nbsp; &nbsp; &nbsp; &nbsp; ></div>&nbsp; &nbsp; &nbsp; &nbsp; <div&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; className="hand"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; style={{background: "red",&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; height: "30%",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; transform: `rotate(${computeHourDeg(date)}deg)`}}&nbsp; &nbsp; &nbsp; &nbsp; ></div>&nbsp; &nbsp; &nbsp; </div>&nbsp; &nbsp; &nbsp; <h3>&nbsp; &nbsp; &nbsp; &nbsp; {pad(date.getHours())}:&nbsp; &nbsp; &nbsp; &nbsp; {pad(date.getMinutes())}:&nbsp; &nbsp; &nbsp; &nbsp; {pad(date.getSeconds())}&nbsp; &nbsp; &nbsp; </h3>&nbsp; &nbsp; </Fragment>&nbsp; );};ReactDOM.render(<Clock />, document.body);</script><script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>下面是一个带有模拟对象的加速版本,用于说明它工作正常:Date.hand {&nbsp; width: 2px;&nbsp; height: 40%;&nbsp; background-color: black;&nbsp; transform-origin: top center;&nbsp; position: absolute;&nbsp; border-radius: 3px;&nbsp; top: 50%;&nbsp; left: 50%;}.analog-clock {&nbsp; position: relative;&nbsp; border-radius: 50%;&nbsp; border: 1px solid #aaa;&nbsp; height: 120px;&nbsp; width: 120px;}<script type="text/babel" defer>const {Fragment, useEffect, useState, useRef} = React;const speedMS = 5;class MockDate {&nbsp; static second = 0;&nbsp; static minute = 0;&nbsp; static hour = 0;&nbsp;&nbsp;&nbsp; constructor() {&nbsp; &nbsp; this.second = MockDate.second;&nbsp; &nbsp; this.minute = MockDate.minute;&nbsp; &nbsp; this.hour = MockDate.hour;&nbsp; }&nbsp;&nbsp;&nbsp; getSeconds() {&nbsp; &nbsp; return this.second;&nbsp; }&nbsp;&nbsp;&nbsp; getMinutes() {&nbsp; &nbsp; return this.minute;&nbsp; }&nbsp;&nbsp;&nbsp; getHours() {&nbsp; &nbsp; return this.hour || 12;&nbsp; }}setInterval(() => {&nbsp; if (++MockDate.second === 60) {&nbsp; &nbsp; MockDate.second = 0;&nbsp; &nbsp; if (++MockDate.minute === 60) {&nbsp; &nbsp; &nbsp; MockDate.minute = 0;&nbsp; &nbsp; &nbsp; MockDate.hour = (MockDate.hour + 1) % 12;&nbsp; &nbsp; }&nbsp; }}, speedMS);const Clock = () => {&nbsp; const [date, setDate] = useState(new MockDate());&nbsp; const requestRef = useRef();&nbsp; let prevDate = null;&nbsp; const tick = () => {&nbsp; &nbsp; const now = new MockDate();&nbsp; &nbsp; if (prevDate && now.getSeconds() !== prevDate.getSeconds()) {&nbsp; &nbsp; &nbsp; setDate(now);&nbsp; &nbsp; }&nbsp; &nbsp; prevDate = now;&nbsp; &nbsp; requestRef.current = requestAnimationFrame(tick);&nbsp; };&nbsp; useEffect(() => {&nbsp; &nbsp; requestRef.current = requestAnimationFrame(tick);&nbsp; &nbsp; return () => cancelAnimationFrame(requestRef.current);&nbsp; }, []);&nbsp; const pad = n => n.toString().padStart(2, 0);&nbsp; const computeHourDeg = date =>&nbsp;&nbsp; &nbsp; (date.getHours() + ~~(date.getMinutes() / 10) * 10 / 60) * 30 + 180&nbsp; ;&nbsp; return (&nbsp; &nbsp; <Fragment>&nbsp; &nbsp; &nbsp; <div className="analog-clock">&nbsp; &nbsp; &nbsp; &nbsp; <div&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; className="hand"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; style={{transform: `rotate(${6 * date.getSeconds() + 180}deg)`}}&nbsp; &nbsp; &nbsp; &nbsp; ></div>&nbsp; &nbsp; &nbsp; &nbsp; <div&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; className="hand"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; style={{transform: `rotate(${6 * date.getMinutes() + 180}deg)`}}&nbsp; &nbsp; &nbsp; &nbsp; ></div>&nbsp; &nbsp; &nbsp; &nbsp; <div&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; className="hand"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; style={{background: "red",&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; height: "30%",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; transform: `rotate(${computeHourDeg(date)}deg)`}}&nbsp; &nbsp; &nbsp; &nbsp; ></div>&nbsp; &nbsp; &nbsp; </div>&nbsp; &nbsp; &nbsp; <h3>&nbsp; &nbsp; &nbsp; &nbsp; {pad(date.getHours())}:&nbsp; &nbsp; &nbsp; &nbsp; {pad(date.getMinutes())}:&nbsp; &nbsp; &nbsp; &nbsp; {pad(date.getSeconds())}&nbsp; &nbsp; &nbsp; </h3>&nbsp; &nbsp; </Fragment>&nbsp; );};ReactDOM.render(<Clock />, document.body);</script><script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script><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>
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JavaScript