如何使倒数计时器反应

时间:2019-12-12 20:57:03

标签: javascript reactjs react-hooks countdown

我正在尝试通过反应做倒数计时器。从10到0基本上是倒数计时,当0时我将调用某个函数。

我理想地找到了一些例子:https://codesandbox.io/s/0q453m77nw?from-embed 但这是一个类组件,我不想使用功能组件和挂钩来做到这一点,但是我做不到。

我尝试过:

function App() {
  const [seconds, setSeconds] = useState(10);
  useEffect(() => {
    setSeconds(setInterval(seconds, 1000));
  }, []);

  useEffect(() => {
    tick();
  });

  function tick() {
    if (seconds > 0) {
      setSeconds(seconds - 1)
    } else {
      clearInterval(seconds);
    }
  }

  return (

    <div className="App">
      <div
        {seconds}
      </div>
    </div>
  );
}

export default App;

它很快就会从10倒数到0,而不是10秒。 我在哪里弄错了?

3 个答案:

答案 0 :(得分:2)

似乎有多个useEffect挂钩导致倒计时每秒运行一次以上。

这是一个简化的解决方案,我们在seconds钩中检查useEffect并选择以下一项:

  • 在1秒后使用setTimeout更新seconds,或者
  • 执行其他操作(您想在倒计时结束时调用的函数)

function App() {
  const [seconds, setSeconds] = React.useState(10);

  React.useEffect(() => {
    if (seconds > 0) {
      setTimeout(() => setSeconds(seconds - 1), 1000);
    } else {
      setSeconds('BOOOOM!');
    }
  });

  return (
    <div className="App">
      <div>
        {seconds}
      </div>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script>

<div id="root"></div>

答案 1 :(得分:1)

您在乎精度吗?如果是这样,则不需要setInterval。如果您不关心精度(也可能不关心),则可以按一定的时间间隔安排对tick()的呼叫,而不是相反。

const TimeoutComponent extends Component {
  constructor(props) {
    super(props);
    this.state = { countdown: 10 };
    this.timer = setInterval(() => this.tick(), props.timeout || 10000);
  }

  tick() {
    const current = this.state.countdown;
    if (current === 0) {
      this.transition();
    } else {
      this.setState({ countdown: current - 1 }); 
    } 
  }

  transition() {
    clearInterval(this.timer);
    // do something else here, presumably.
  }

  render() {
    return <div className="timer">{this.state.countDown}</div>;
  }
}

答案 2 :(得分:0)

这取决于您的逻辑。在当前情况下,您运行useEffect方法的tick在每个渲染器上运行。您可以在下面找到一个简单的示例。

function App() {
  const [seconds, setSeconds] = useState(10);
  const [done, setDone] = useState(false);
  const foo = useRef();

  useEffect(() => {
    function tick() {
        setSeconds(prevSeconds => prevSeconds - 1)
    }
    foo.current = setInterval(() => tick(), 1000)
  }, []);

  useEffect(() => {
    if (seconds  === 0) {
      clearInterval(foo.current);
      setDone(true);
    }
  }, [seconds])

  return (

    <div className="App">
    {seconds}
    {done && <p>Count down is done.</p>}
    </div>
  );
}

首先要进行倒数计时。由于interval会使用回调一来设置状态,因此会创建一个闭包。在第二个效果中,我们正在检查条件。