Ревью и рефакторинг таймера в React‑компоненте

Таймер лучше создавать и очищать в одном эффекте, а подсветку выводить из state. Прямой querySelector и вечное добавление класса здесь не нужны.

Короткий ответ

Что ответить на собеседовании

Таймер лучше создавать и очищать в одном эффекте, а подсветку выводить из 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, а класс подсветки не снимался. Этот вариант считает тики; для точного секундомера следует вычислять прошедшее время по монотонным часам.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее