簡體   English   中英

clearInterval 在使用功能組件的 React 應用程序中不起作用

[英]clearInterval not working in React Application using functional component

我想使用functional componentReact中構建一個計時器應用程序,以下是要求。

該組件將顯示一個初始化為0的數字,稱為counter

該組件將在counter編號下方顯示一個Start按鈕。

單擊“ Start ”按鈕后,計數器將開始運行。 這意味着counter編號將開始每 1 秒遞增 1。

當計數器運行(遞增)時, Start按鈕將變為Pause按鈕。

單擊Pause按鈕時, counter將保留其值(數字)但停止運行(遞增)。

該組件還將顯示一個Reset按鈕。 單擊Reset按鈕后, counter將 go 恢復為其初始值(在我們的示例中為0 )並停止運行(遞增)。

下面是我已經實現的代碼,但clearInterval似乎沒有工作,另外我如何實現重置按鈕?

代碼:

import React, { useState, useEffect } from "react";

export default function Counter() {
  const [counter, setCounter] = useState(0);
  const [flag, setFlag] = useState(false);
  const [isClicked, setClicked] = useState(false);
  var myInterval;

  function incrementCounter() {
    setClicked(!isClicked);
    if (flag) {
      myInterval = setInterval(
        () => setCounter((counter) => counter + 1),
        1000
      );
      setFlag(false);
    } else {
      console.log("sasdsad");
      clearInterval(myInterval);
    }
  }

  function resetCounter() {
    clearInterval(myInterval);
    setCounter(0);
  }

  useEffect(() => {
    setFlag(true);
  }, []);

  return (
    <div>
      <p>{counter}</p>
      <button onClick={incrementCounter}>
        {isClicked ? "Pause" : "Start"}
      </button>
      <button onClick={resetCounter}>Reset</button>
    </div>
  );
}

Codesandbox 鏈接: CodeSandbox

我做了一個稍微不同的版本,它使用了一個在isRunning上運行的額外useEffect (從flag更改了名稱)更改:

import React, { useState, useEffect, useRef } from "react";

export default function Counter() {
  const [counter, setCounter] = useState(0);
  // Change initial value to `false` if you don't want
  // to have timer running on load
  // Changed `flag` name to more significant name
  const [isRunning, setIsRunning] = useState(false);
  // You don't need 2 variable for this
  //const [isClicked, setClicked] = useState(false);

  // Using `useRef` to store a reference to the interval
  const myInterval = useRef();

  useEffect(() => {
    // You had this line to start timer on load
    // but you can just set the initial state to `true`
    //setFlag(true);
    // Clear time on component dismount
    return () => clearInterval(myInterval.current);
  }, []);

  // useEffect that start/stop interval on flag change
  useEffect(() => {
    if (isRunning) {
      myInterval.current = setInterval(
        () => setCounter((counter) => counter + 1),
        1000
      );
    } else {
      clearInterval(myInterval.current);
      myInterval.current = null;
    }
  }, [isRunning]);

  // Now on click you only change the flag
  function toggleTimer() {
    setIsRunning((isRunning) => !isRunning);
  }

  function resetCounter() {
    clearInterval(myInterval.current);
    myInterval.current = null;
    setCounter(0);
    setIsRunning(false);
  }

  return (
    <div>
      <p>{counter}</p>
      <button onClick={toggleTimer}>{isRunning ? "Pause" : "Start"}</button>
      <button onClick={resetCounter}>Reset</button>
    </div>
  );
}

演示:https://codesandbox.io/s/dank-night-wwxqz3?file=/src/Counter.js

作為額外的一點,我制作了一個使用自定義鈎子useTimer的版本。 這樣,組件代碼就更干凈了: https://codesandbox.io/s/agitated-curie-nkjf62?file=/src/Counter.js

使用useRef將區間作為參考。 然后使用resetCounter()清理間隔參考。

const intervalRef = useRef(null)

const incrementCounter = () => {
  intervalRef.current = setInterval(() => {
    setCounter(prevState => prevState + 1)
  }, 1000);
};

const resetCounter = () => {
  clearInterval(intervalRef.current);
  intervalRef.current = null;
};

您必須將myInterval存儲在 state 中。 之后,當單擊按鈕且flagfalse時,您可以清除間隔(狀態為 myInterval)。

在每次渲染之間,您的變量myInterval值無法生存。 這就是為什么您需要使用 [ useRef ][1] 鈎子在每次渲染中保存此變量的引用。

此外,您不需要標志 function,因為您擁有myClicked變量的所有信息

這是使用這些修改對您的代碼進行的修改。 如果您有任何問題,請不要猶豫。

import React, { useState, useEffect, useRef } from "react";

export default function Counter() {
  const [counter, setCounter] = useState(0);
  const [isStarted, setIsStarted] = useState(false);
  const myInterval = useRef();

  function start() {
    setIsStarted(true);
      myInterval.current = setInterval(() => setCounter((counter) => counter + 1), 100);
      100;
    } 

  function pause() {
    setIsStarted(false);
    clearInterval(myInterval.current);
  }

  function resetCounter() {
    clearInterval(myInterval.current);
    setCounter(0);
  }

  return (
    <div>
      <p>{counter}</p>
      {!isStarted ? 
      <button onClick={start}>
        Start
      </button> 
      :
      <button onClick={pause}>
        Pause
      </button> 
    }
      
      <button onClick={resetCounter}>Reset</button>
    </div>
  );
}
\\\


  [1]: https://reactjs.org/docs/hooks-reference.html#useref

我會把這個留給有同樣問題的人。

就我而言,問題是使用節點 setInterval 而不是 window.setInterval。

這是一個問題,因為這會返回一個 Node.Timer 類型,它是 object 而不是數字(setInterval ID),因為 clearInterval() 需要一個參數類型的數字才能工作。 所以要解決這個問題,

React.useEffect(() => {
 let timeoutId;
 timeoutId = window.setInterval(callback, 100);

 return = () => {
  if(timeoutId) clearInterval(timeoutId)
 }
}, [])

或在 class 組件中使用 componentWillMount()

暫無
暫無

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

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