Таймер лучше создавать и очищать в одном эффекте, а подсветку выводить из state. Прямой querySelector и вечное добавление класса здесь не нужны.
Ревью и рефакторинг таймера в React‑компоненте
Таймер лучше создавать и очищать в одном эффекте, а подсветку выводить из state. Прямой querySelector и вечное добавление класса здесь не нужны.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
В данном задании представлен React‑компонент, реализующий простой таймер. Требуется провести код‑ревью и выполнить рефакторинг, улучшив читаемость и структуру кода, а также избавиться от потенциальных проблем.
Что нужно сделать:
- Проанализировать текущую реализацию, выявить места, где код можно упростить или сделать более надёжным.
- Переписать компонент, используя более понятные имена переменных и функций, избавиться от дублирования логики.
- При необходимости добавить комментарии, улучшить работу с эффектами и очисткой ресурсов.
- Сохранить функциональность: кнопка «Start/Pause» должна запускать и приостанавливать счёт, кнопка «Stop» — сбрасывать таймер, а каждый пятый тик должен визуально подсвечиваться.
import { useEffect, useRef, useState } from "react";
import "./App.css";
export default function App() {
const [isRunning, setIsRunning] = useState(false);
const [seconds, setSeconds] = useState(0);
const timerRef = useRef();
const handleStop = () => {
setSeconds(0);
setIsRunning(false);
clearInterval(timerRef.current);
timerRef.current = null;
};
const handleToggle = () => {
if (isRunning) {
clearInterval(timerRef.current);
timerRef.current = null;
} else {
timerRef.current = setInterval(() => {
setSeconds((prev) => prev + 1);
}, 1000);
}
setIsRunning(!isRunning);
};
useEffect(() => {
if (seconds % 5 === 0 && seconds !== 0) {
document.querySelector(".timer").classList.add("pulsate");
}
}, [seconds]);
return (
<div className="main">
<div>
<button onClick={handleToggle}>{isRunning ? "Pause" : "Start"}</button>
<button onClick={handleStop}>Stop</button>
</div>
<div className="timer">{seconds}</div>
</div>
);
}
Ожидаемый результат: После рефакторинга код должен быть проще для восприятия, не содержать лишних побочных эффектов и корректно работать во всех браузерах.
Ответ
Таймер лучше создавать и очищать в одном эффекте, а подсветку выводить из state. Прямой querySelector и вечное добавление класса здесь не нужны.
function App() {
const [running, setRunning] = React.useState(false);
const [seconds, setSeconds] = React.useState(0);
React.useEffect(() => {
if (!running) return;
const timer = setInterval(() => setSeconds(value => value + 1), 1000);
return () => clearInterval(timer);
}, [running]);
const highlighted = seconds > 0 && seconds % 5 === 0;
return <div className='main'>
<button onClick={() => setRunning(value => !value)}>{running ? 'Pause' : 'Start'}</button>
<button onClick={() => { setRunning(false); setSeconds(0); }}>Stop</button>
<div className={highlighted ? 'timer pulsate' : 'timer'}>{seconds}</div>
</div>;
}
Исходный интервал не очищался при unmount, а класс подсветки не снимался. Этот вариант считает тики; для точного секундомера следует вычислять прошедшее время по монотонным часам.