Почему setInterval не работает правильно в приложении React

Я хочу создать обратный таймер при нажатии кнопки.

Я создал setInterval с функцией, которая должна учитывать данные секунд и минут, а при нажатии на кнопку запуска секунды = 59 и уменьшаются на 1 каждую секунду, после этого при секундах = 0 минуты должны уменьшаться на 1, а секунд 59. Но после нажатия кнопки секунды = 59 а минуты -1, но обратного отсчета не происходит

  const [time, setTime] = useState(10);
  const [sec, setSec] = useState(0);
  const [min, setMin] = useState(10);

  let timerInt;

  const timer=()=> {
    timerInt = setInterval(timerF, 1000);
    function timerF() {
      if (sec===0){
        setSec(59);
        setMin(min - 1);
      }
      if (sec <= 0 && min <= 0) {
        clearInterval(timerInt);
        setSec(0);
        setMin(0);
        console.info('finish')
      }
      setSec(prev=>prev-1)
    }
  }
  const handleStart = (time) => {
    setMin(time)
    setTime(time)
    timer();
    setDisabledBtn(true);
  };
      <div className = "time">
        <div className = "time-min">{min}</div>
        <span>:</span>
        <div className = "time-sec">{sec}</div>
      </div>
      <button
        className = "start-btn active-btn"
        type = "button"
        disabled = {disabledBtn && 'disabled'}
        onClick = {() => handleStart(10)}
      >
        START
      </button>

FYI setInterval не является точным.

epascarello 09.05.2023 17:17
timerInt тоже должен быть частью состояния, наверное.
user3840170 09.05.2023 17:36

Если min это 1 и вы звоните setMin(min - 1)min все равно будет 1. Он будет равен нулю в следующем цикле рендеринга.

Konrad 09.05.2023 17:45

Отвечает ли это на ваш вопрос? Метод set useState не сразу отражает изменение

Konrad 09.05.2023 17:45
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
4
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В вашем случае setInterval не имеет обновленного значения состояния реакции. Поэтому я создал аналогичную функциональность, используя useEffect и вызывая функцию timer() с обновленным состоянием реакции. Я использовал setTimeout, потому что мы привязываем и очищаем setTimeout при каждом обновлении состояния. Так что нет смысла использовать setInterval в моем случае использования.

Обновленный и рабочий код ниже:

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

export function App(props) {
   const [time, setTime] = useState(2);
  const [sec, setSec] = useState(0);
  const [min, setMin] = useState(2);
  const [startTimer, setStartTimer] = useState(false);

  let timerInt;

  useEffect(() => {
    if (startTimer) {
      timer();
    }
    return () => {
      clearTimeout(timerInt);
    }
  }, [min, sec, startTimer]);

  const timer = () => {

    const timerF = () => {
      console.info("timer1", sec, min);
      if (!sec){
        setSec(59);
        setMin(min - 1);
      }
      if (sec <= 0 && min <= 0) {
        clearTimeout(timerInt);
        setSec(0);
        setMin(0);
        setStartTimer(false);
        console.info('finish')
      }
      setSec(prev=>prev-1)
    }
    timerInt = setTimeout(timerF, 1000);
  }

  const handleStart = (time) => {
    setStartTimer(true);
    setMin(time)
    setTime(time);
  };
   return (
    <div>
    <div className = "time">
        <div className = "time-min">{min}</div>
        <span>:</span>
        <div className = "time-sec">{sec}</div>
    </div>
      <button
        className = "start-btn active-btn"
        type = "button"
        onClick = {() => handleStart(2)}
      >
        START
      </button>
      </div>
   )

}

Попробуйте и дайте мне знать, если у вас все еще есть вопросы / проблемы.

Я немного модифицировал его, потому что в конце, когда обратный отсчет закончился, в поле секунд на экране было -1. Я исправил и все работает как надо! Спасибо!

wch 09.05.2023 18:10

Другие вопросы по теме