Clear Interval不使用功能组件在React应用程序中工作

发布于 2025-01-22 18:33:07 字数 1745 浏览 2 评论 0 原文

我想在 react> react 使用功能组件及以下构建计时器应用程序。

该组件将显示一个初始化为 0 的数字已知为 Counter

该组件将在 Counter 编号下方显示开始按钮。

单击开始按钮,计数器将开始运行。这意味着 Counter 号码将每秒开始增加1个。

当计数器运行(增量)时, start 按钮将成为暂停按钮。

单击暂停按钮时, counter 将保留其值(数字),但停止运行(增量)。

组件还将显示重置按钮。 单击重置按钮时,计数器将转到其初始值(在我们的情况下为 0 )并停止运行(增量)。

以下是我已实施的代码,但是 clear Interval 似乎无法正常工作,还如何实现重置按钮?

代码:

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

I wanted to build a timer application in React using functional component and below are the requirements.

The component will display a number initialized to 0 know as counter.

The component will display a Start button below the counter number.

On clicking the Start button the counter will start running. This means the counter number will start incrementing by 1 for every one second.

When the counter is running(incrementing), the Start button will become the Pause button.

On clicking the Pause button, the counter will preserve its value (number) but stops running(incrementing).

The component will also display a Reset button.
On clicking the Reset button, the counter will go to its initial value(which is 0 in our case) and stops running(incrementing).

Below is the code that I have implemented, but clearInterval doesn't seems to be working, Also how do i implement Reset Button?

Code:

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 link:
CodeSandbox

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

初熏 2025-01-29 18:33:07

我做了一个稍微不同的版本,该版本使用额外的使用 iSrunning 上运行(从 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>
  );
}

demo: https://codesandbox.io/s/dank-night-night-wwxqz3?file= /src/counter.js

作为一个额外的额外,我制作了一个使用自定义挂钩 usetimer 的版本。通过这种方式,组件代码是清洁的:

I did a slightly different version that use an extra useEffect that runs on isRunning (changed name from flag) change:

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>
  );
}

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

As a little extra i've made a version that uses a custom hook useTimer. In this way the component code is way cleaner:
https://codesandbox.io/s/agitated-curie-nkjf62?file=/src/Counter.js

蛮可爱 2025-01-29 18:33:07

使用 useref 将间隔作为参考。然后使用 resetCounter()清洁间隔参考。

const intervalRef = useRef(null)

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

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

Use useRef to make the interval as a ref. Then use resetCounter() to clean the interval ref.

const intervalRef = useRef(null)

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

const resetCounter = () => {
  clearInterval(intervalRef.current);
  intervalRef.current = null;
};
旧人 2025-01-29 18:33:07

我将把它留在这里给任何遇到同样问题的人。

就我而言,问题是使用节点setInterval而不是window.setInterval。

这是一个问题,因为这返回一种node.timer,它是一个对象而不是数字(setInterval ID),以使clearInterval()在需要数字的参数类型时工作。因此,要解决此问题,

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

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

或在类组件中使用componentwillmount()

I'll just leave this here for anyone having the same problem.

in my case, the issue was node setInterval was used instead of window.setInterval.

this is a problem since this returns a type of Node.Timer which is an object instead of number (setInterval ID) for the clearInterval() to work as it needs an argument type of number. so to fix this,

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

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

or in class components use componentWillMount()

三岁铭 2025-01-29 18:33:07

在每个渲染变量 MyInterval 值之间都无法生存。这就是为什么您需要使用[ useref ] [1]挂钩,以在每个渲染中保存此变量的引用。

此外,您不需要标志功能,因为您拥有所有信息,其中包括 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

Between each rendering your variable myInterval value doesn't survive. That's why you need to use the [useRef][1] hook that save the reference of this variable across each rendering.

Besides, you don't need an flag function, as you have all information with the myClicked variable

Here is a modification of your code with those modifications. Don't hesitate if you have any question.

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
冷血 2025-01-29 18:33:07

您必须在状态中存储 myinterval 。之后,当按钮单击时, flag false ,您可以清除间隔(状态下的myinterval)。

You have to store myInterval in state. After that when button is clicked and flag is false, you can clear interval (myInterval in state).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文