簡體   English   中英

用鈎子構建一個 React 時鍾

[英]Building a React clock with hooks

我正在制作一個可以計數的時鍾,只是一個練習(我知道有更好的方法可以做到這一點)。

我的問題是當添加一分鍾時,“addMinute”似乎運行了兩次,而我終其一生都無法弄清楚如何或為什么。

這是關於代碼沙盒的演示: https://codesandbox.io/s/restless-frost-bud7p這里是代碼一目了然:

(請注意,計數器最多只能計數 3 並且計數更快;這僅用於測試目的)

const Clock = (props) => {

    const [seconds, setSeconds] = useState(0)
    const [minutes, setMinutes] = useState(0)
    const [hours, setHours] = useState(0)

    const addHour = () => {
        setHours(p => p + 1)
    }
    const addMinute = () => {
        setMinutes(prev => {
            if (prev === 3) {
                addHour()
                return 0
            } else {
                return prev + 1
            }
        })
    }
    const addSecond = () => {
        setSeconds(prevState => {
            if (prevState === 3) {
                addMinute()
                return 0
            } else {
                return prevState + 1
            }
        })
    }
    
    

    useEffect(() => {
        const timer = setInterval(addSecond, 600)
        return () => clearInterval(timer)
    }, [])

    return (
        <div>
            <h1>time!</h1>
            <p>
                {hours < 10 ? 0 : ""}{hours}
                :
                {minutes < 10 ? 0 : ""}{minutes}
                :
                {seconds < 10 ? 0 : ""}{seconds}
            </p>
            <p>
                {seconds.toString()}
            </p>
        </div>
    )
}

問題是您在 index.js 文件中使用了 React.StrictMode 包裝器。

嚴格模式無法自動為您檢測副作用,但可以通過使它們更具確定性來幫助您發現它們。 這是通過有意雙重調用以下函數來完成的: https://reactjs.org/docs/strict-mode.html#detecting-unexpected-side-effects

所以你應該決定是使用嚴格模式還是有副作用,簡單的方法就是刪除 React.StrictMode 包裝器。 另一種方法是消除副作用,您只需要執行以下操作:

將您的 addSecond 和 addMinute 函數更新為:

const addMinute = () => {
  setMinutes((prev) => prev + 1);
};

const addSecond = () => {
  setSeconds((prevState) => prevState + 1);
};

您的 useEffect 調用如下:

useEffect(() => {
  if(seconds === 3) {
    addMinute();
    setSeconds(0);
  };

  if(minutes === 3) {
    addHour();
    setMinutes(0);
  } 

  const timer = setInterval(addSecond, 600);
  return () => clearInterval(timer);
}, [seconds, minutes]);

這里是您的代碼的更新版本: https://codesandbox.io/s/goofy-lake-1i9xf

幾個問題,

首先你需要使用prev幾分鍾,所以

const addMinute = () => {
    setMinutes(prev => {
        if (prev === 3) {
            addHour()
            return 0
        } else {
            return prev + 1
        }
    })
}

然后您需要從索引中刪除React.StrictMode包裝器組件,這實際上是導致雙倍增加的原因,因為嚴格模式所做的一部分是

這是通過有意雙重調用以下函數來完成的:

Class 組件constructorrendershouldComponentUpdate方法
Class 組件 static getDerivedStateFromProps方法
Function 組件體
State 更新函數( setState的第一個參數)
傳遞給useStateuseMemouseReducer的函數

見: https://codesandbox.io/s/pensive-wildflower-pujmk

所以我不知道嚴格模式和有意的雙重渲染。 閱讀文檔后,我終於明白了這樣做的目的。

因此,似乎最好的解決方案是使 useEffect 沒有副作用,而是在效果之外處理該邏輯,但仍然每秒都在變化。

所以,我設置了一個效果,它有一塊 state 從零開始,每秒上升一個。

然后,隨着“時間”的每次變化,useMemo 將重新計算總時間的小時數、分鍾數和秒數。

我唯一不喜歡的是每次渲染都運行這些計算(但實際上這些計算只需要幾毫秒。所以性能似乎不是問題)。

    const [time, setTime] = useState(0)

    useEffect(() => {
        const timer = setInterval(() => {
            setTime(p => p + 1)
        }, 999);
        return () => clearTimeout(timer);
    }, [userState]);

    const timeJSON = useMemo(() => {
        const hrs = Math.floor(time/3600)
        const mins = Math.floor( (time-(3600*hrs)) / 60 )
        const secs = time - (3600 * hrs) - (60*mins)

        return {
            hrs,
            mins,
            secs,
        }

    }, [time])

    return (
        <div>            
            <p>             
                {timeJSON.hrs < 10 ? 0 : ""}{timeJSON.hrs}
                :
                {timeJSON.mins < 10 ? 0 : ""}{timeJSON.mins}
                :
                {timeJSON.secs < 10 ? 0 : ""}{timeJSON.secs}
                
            </p>
        </div>
    )

再次感謝大家為我指出正確的方向!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM