onClick уменьшает число с каждым щелчком мыши, а не запускает таймер

#javascript #html #css #reactjs #jsx

#javascript #HTML #css #reactjs #jsx

Вопрос:

Итак, я создаю приложение для медитации в React. Для начала я дал ему 25-минутный обратный отсчет.

Я хочу, чтобы обратный отсчет начинался при нажатии кнопки. Однако, когда я переношу setInterval в функцию handleClick(), секунды уменьшаются за клик, а не запускают обратный отсчет один раз.

Любая помощь приветствуется, заранее спасибо.

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


function Timer() {
  const [minutes, setMinutes] = useState(25);
  const [seconds, setSeconds] = useState(0);

  function handleClick() {
    console.log('Clicked');
    let interval = setInterval(() => {
      clearInterval(interval);
      if (seconds === 0) {
        if (minutes !== 0) {
          setSeconds(59);
          setMinutes(minutes - 1);
      } else {
        setSeconds(seconds - 1);
      }
    }, 1000)
  }

  const timerMinutes = minutes < 10 ? `0${minutes}`: minutes;
  const timerSeconds = seconds < 10 ? `0${seconds}` : seconds;

  return (
    <>
      <div className="grey-box">
        <div className="number-box">
          <h2 className="timer-text">{timerMinutes}:{timerSeconds}</h2>
        </div>
        <div className="buttons-container">
          <button className="timer-button" onClick={handleClick}>
            <img 
              className="play" 
              src="https://img.icons8.com/ios-glyphs/90/ffffff/play--v1.png" 
            />
          </button>
        </div>
      </div>
    </>
    );
  }

export default Timer;
 

Комментарии:

1. Вы немедленно очищаете интервал без условия, т.Е. Очищаете, не проверяя, завершен ли обратный отсчет

Ответ №1:

Вы создаете таймер гораздо более сложным способом. Я сделал образец, который может охватить этот вариант использования вместе с некоторыми крайними случаями.

Ссылка на Codesandbox

 import React, { useState, useEffect, useRef } from "react";
import ReactDOM from "react-dom";
import "./styles.css";

function ClearTimer(TimerHndlRef) {
    clearInterval(TimerHndlRef.current);
    TimerHndlRef.current = null;
}
function App() {
    const TimerHndlRef = useRef();
    const [seconds, secondsSet] = useState(5);
    const onClick = () => {
        if (TimerHndlRef.current) {
            ClearTimer(TimerHndlRef);
            return;
        }
        TimerHndlRef.current = setInterval(
            () =>
                secondsSet((s) => {
                    const v = s - 1;
                    if (v > 0) return v;
                    ClearTimer(TimerHndlRef);
                    return 0;
                }),
            1000,
        );
    };
    const onChange = (e) => secondsSet(parseInt(e.target.value, 10));
    useEffect(() => {
        return () => {
            if (!TimerHndlRef.current) return;
            ClearTimer(TimerHndlRef);
        };
    }, []);
    const mLeft = Math.floor(seconds / 60);
    const sLeft = seconds - mLeft * 60;
    const mDisp = mLeft < 10 ? `0${mLeft}` : mLeft;
    const sDisp = sLeft < 10 ? `0${sLeft}` : sLeft;
    return (
        <div>
            <p>
                <input type="number" value={seconds} onChange={onChange} /> = {mDisp}m  {" "}
                {sDisp}s
            </p>
            <button onClick={onClick}>Test</button>
        </div>
    );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
 

Ответ №2:

С каждым щелчком мыши вы объявляете, что вычитается минута:

 setMinutes(minutes - 1); 

Если вы хотите, чтобы таймер был установлен только один раз, вы можете проверить, был ли он уже установлен, и если да, прервите функцию handleClick . Объявление let для таймера будет доступно только в этом блоке кода (для каждого запуска). Поэтому, когда произойдет еще один щелчок, у вас не будет доступа к таймеру. Используйте «this.interval» (область действия функции) или «var interval» (глобальная / оконная область), и тогда вы сможете проверить в верхней части функции handleClick, установлен ли таймер.

 if (this.interval) return;

OR

if (interval) return;