Считайте остаток относительно времени завершения, а не вычитайте фиксированное число на каждом тике. Reset должен заново установить начальный срок.
Компонент обратного отсчёта с кнопкой восстановления
Считайте остаток относительно времени завершения, а не вычитайте фиксированное число на каждом тике. Reset должен заново установить начальный срок.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Создайте React‑компонент Countdown, который: – получает prop startTime (в миллисекундах), – автоматически начинает уменьшать значение до нуля, – не позволяет значению стать отрицательным, – выводит оставшееся время на экран, – имеет кнопку Reset, возвращающую счётчик к исходному значению.
import "./styles.css";
import Countdown from "./Countdown";
export default function App() {
return (
<div className="App">
<Countdown startTime={5000} />
</div>
);
}
Ответ
Считайте остаток относительно времени завершения, а не вычитайте фиксированное число на каждом тике. Reset должен заново установить начальный срок.
function Countdown({ startTime }) {
const duration = Number.isFinite(startTime) ? Math.max(0, startTime) : 0;
const [left, setLeft] = React.useState(duration);
const [restart, setRestart] = React.useState(0);
React.useEffect(() => {
const end = performance.now() + duration;
let timer;
function tick() {
const remaining = Math.max(0, end - performance.now());
setLeft(remaining);
if (remaining > 0) timer = setTimeout(tick, Math.min(100, remaining));
}
tick();
return () => clearTimeout(timer);
}, [duration, restart]);
return <div>
<output>{Math.ceil(left)} мс</output>
<button onClick={() => { setLeft(duration); setRestart(value => value + 1); }}>Reset</button>
</div>;
}
При изменении startTime отсчёт перезапускается. Очистка эффекта отменяет старый таймер; throttling вкладки не должен накапливать ошибку простого вычитания по тикам.