В этом уроке я покажу вам, как добавить секундомер в приложение React.

Шаг 1. Создайте базовый макет в компоненте «Секундомер».

В приведенном выше секундомере отображение времени состоит из 4 частей: сантисекунд, секунд, минут и часов. Под дисплеем находятся две кнопки управления: СТАРТ/ПАУЗА и СБРОС. Достаточно просто.

Вот как выглядит компонент Секундомер после определения базового макета:

// Stopwatch.js
function Stopwatch() {
    return (
        <div className="container">
            <div className="stopwatch">
                <div className="display">
                    <span className="time">
                        0<span className="unit">h</span>
                        &nbsp;
                        00<span className="unit">m</span>
                        &nbsp;
                        00<span className="unit">s</span>
                        &nbsp;
                        00
                    </span>
                </div>
                <div className="control">
                    <button>START</button>
                    <button>RESET</button>
                </div>
            </div>
        </div>
    )
}

export default Stopwatch
/* style.css */
* {
    box-sizing: border-box;
}

.container {
    display: flex;
    justify-content: center;
    align-items: center;
}

.stopwatch {
    max-width: 500px;

    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: 30px;
    
    padding: 30px;

    background-color: rgb(236, 234, 234);
    border-radius: 5px;
    box-shadow: .5px .5px 2px rgb(149, 149, 149);
}

.display {
    font-size: 3rem;
}

span.unit {
    font-size: 2rem;
    font-weight: lighter;
}

.control {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 40px;
}

.control > button {
    width: 80px;
    padding: 4px;
    font: inherit;
    font-size: .9rem;
    border: none;
    border-radius: 5px;
    color: white;
    background-color: rgb(3, 118, 3);
    cursor: pointer;
}

Шаг 2: Отображение времени в 4 единицах

На этом этапе есть небольшой расчет времени. Прямо сейчас на панели отображения мы просто заполнили время заполнителями (00). Представьте, что здесь передается фактическое значение времени, нам пришлось бы отображать его в 4 единицах: сантисекунды, секунды, минуты и часы. Поэтому я создал 4 функции для их вычисления. См. подробное объяснение каждой функции в приведенном ниже коде.

// time is a millisecond value.
// hourPart only contains hour value of the time.
// this is the highest unit of the time
const hourPart = (time) => {
    return Math.floor(time / (60 * 60 * 1000))
}

// time is a millisecond value.
// minutePart only contains minute value of the time.
// It will reset to 0 when reaching 60 minutes.
const minutePart = (time) => {
    return Math.floor((time / (60 * 1000)) % 60)
}

// time is a millisecond value.
// secondPart only contains second value of the time.
// It will reset to 0 when reaching 60 seconds.
const secondPart = (time) => {
    return Math.floor((time / 1000) % 60)
}

// time is a millisecond value.
// centiSecondPart only contains centi-second value of the time.
// It will reset to 0 when reaching 100 centi-seconds. 
// (note: centi-second is 1/100 of a second)
const centiSecondPart = (time) => {
    return (time / 10) % 100
}

Шаг 3: Сохраните значение времени в состоянии реакции

После того, как служебные функции для вычисления времени были готовы, я определил время как состояние реакции и передал его оператору return, чтобы отразить изменение значения состояния.

Вот как сейчас выглядит файл Stopwatch.js:

// Stopwatch.js
function Stopwatch() {

    const [time, setTime] = useState(0);

    return (
        <div className="container">
            <div className="stopwatch">
                <div className="display">
                    <span className="time">
                        { hourPart(time) }<span className="unit">h</span>
                        &nbsp;
                        { ("0" + minutePart(time)).slice(-2) }<span className="unit">m</span>
                        &nbsp;
                        { ("0" + secondPart(time)).slice(-2) }<span className="unit">s</span>
                        &nbsp;
                        { ("0" + centiSecondPart(time)).slice(-2) } 
                    </span>
                </div>
                <div className="control">
                    <button>START</button>
                    <button>RESET</button>
                </div>
            </div>
        </div>
    )
}

export default Stopwatch

Шаг 4: Увеличение времени обработки

Логика увеличения времени секундомера реализована с помощью функции setInverval. Он будет выполнять переданную функцию каждые delay миллисекунды. В нашем случае значение времени будет меняться каждые 1 сантисекунду (т. е. 10 миллисекунд), мы можем написать такую ​​функцию:

setInterval(() => {
                setTime(pre => pre + 10);
            }, 10)

Теперь мы помещаем эту логику в хук useEffect и создаем другое состояние, чтобы контролировать, активен ли таймер.

// Stopwatch.js
...
const [active, setActive] = useState(false);

    useEffect(() => {
        let interval = null;
        if (active) {
            // time change every centi-second
            // 1 centi-second = 10 milli-seconds
            interval = setInterval(() => {
                setTime(pre => pre + 10);
            }, 10)
        } else {
            clearInterval(interval)
        }
        
        return () => {
            clearInterval(interval)
        }
    }, [active])
...

Я передал функцию в setTime, таким образом сохранялось предыдущее время, и значение времени продолжало увеличиваться. Нам не нужно вызывать useEffect для повторного рендеринга при каждом изменении значения.

Шаг 5: Реализуйте кнопки управления

Наконец пришло время добавить обработку событий к кнопкам. Когда мы нажмем кнопку СТАРТ, таймер должен быть активирован, а текст кнопки изменится на ПАУЗА. Когда мы нажмем кнопку PAUSE, таймер остановится, а текст кнопки вернется к START.

Кнопка RESET установит значение времени на 0 при деактивации таймера.

// Stopwatch.js
...
    const handleStart = () => {
        setActive(true)
    }

    const handlePause = () => {
        setActive(false)
    }

    const handleReset = () => {
        setTime(0)
        setActive(false)
    }
...
    return (
        <div className="container">
            <div className="stopwatch">
                ...
                <div className="control">
                    {
                        active ? 
                        <button className="red_background" onClick={ handlePause }>PAUSE</button> :
                        <button onClick={ handleStart }>START</button>
                    }
                    <button className="orange_background" onClick={ handleReset }>RESET</button>
                </div>
            </div>
        </div>
    )

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

/* style.css */
...
button.red_background {
    background-color: rgb(210, 9, 9);
}

button.orange_background {
    background-color: rgb(209, 137, 2);
}

Вот и все. Теперь у вас есть секундомер реакции. Я надеюсь, что вы найдете это полезным. Ознакомьтесь с полным исходным кодом здесь: https://github.com/zhna123/mini-apps/tree/main/src/apps/stopwatch