꾸매코딩

[React] state를 활용한 동적인 타이머 만들기 - 시작, 정지, 리셋 기능 구현 본문

Project/Timer Project

[React] state를 활용한 동적인 타이머 만들기 - 시작, 정지, 리셋 기능 구현

꾸매코더 2021. 7. 8. 16:59
반응형

최종 결과

타이머의 시작, 정지, 리셋 기능을 만들었고 정상 작동 중이다.

결과


현재 상황

어제 React Hook 사용이 익숙하지 않다는 것을 뼈저리게 느끼고 Hook 공식 문서를 읽으며 이해하고자 노력했다.

공식문서를 읽는 횟수가 늘어날수록 점점 이해하는 것이 느껴지기 시작했고 읽기 전보다는 조금 더 성장했다고 느껴진다. 

지금까지 타이머타이머의 시작 버튼을 눌렀을 때 시작되는 것까지 만들었기 때문에 오늘 정지리셋 버튼을 만들려고 시도해보았다. 추가적으로 시작 버튼이 중복으로 눌릴 경우 setInterval이 반복해서 작동하는 오류를 수정하려 했다.


설명

updateTimer 함수가 화면에 보이는 타이머의 시간을 변경하는 함수이다.

블로그를 참고하여 시작, 정지, 리셋 기능이 가능한 1초에 1씩 증가하는 count를 얻었다.

count를 updateTimer 함수에 적용하면 끝이었다.

의도 - 1 

return 된 count값을 updateTimer 함수에 적용해주면 Timer가 1초마다 자동으로 랜더링 될 것이라 생각했다

오류 - 1

사용자 정의 Hook인 useCounter 만들었다.

의도는 나의 착각이었다.
const useCounter = (initialValue, ms) => {
    const [count, setCount] = useState(initialValue);
    const intervalRef = useRef(null);
    const start = useCallback(() => {
        if (intervalRef.current !== null) {
            return;
        }
        intervalRef.current = setInterval(() => {
            setCount(c => c + 1);
        }, ms);
    }, []);
    const stop = useCallback(() => {
        if (intervalRef.current === null) {
            return;
        }
        clearInterval(intervalRef.current);
        intervalRef.current = null;
    }, []);
    const reset = useCallback(() => {
        setCount(0);
    }, []);

    return { count, start, stop, reset };
}

export default function SetTimer() {
    const [currentHours, setCurrentHours] = useState(0);
    const [currentMinutes, setCurrentMinutes] = useState(0);
    const [currentSeconds, setCurrentSeconds] = useState(0);
    const { count, start, stop, reset } = useCounter(0, 1000);

    const updateTimer = () => {
        const checkMinutes = Math.floor(count / 60);
        const hours = Math.floor(count / 3600);
        const minutes = checkMinutes % 60;
        const seconds = count % 60;
        setCurrentHours(hours)
        setCurrentSeconds(seconds)
        setCurrentMinutes(minutes)
    }

    return (
        <>
            <h1>{currentHours < 10 ? `0${currentHours}` : currentHours}:{
                currentMinutes < 10 ? `0${currentMinutes}` : currentMinutes}:{
                    currentSeconds < 10 ? `0${currentSeconds}` : currentSeconds}</h1>
            count = {count}
            <button onClick={start}>시작</button>
            <button onClick={stop}>정지</button>
            <button onClick={reset}>초기화</button>
            <button onClick={updateTimer}>타이머 확인</button>
        </>
    )
}

결과 - 1

'타이머 확인' 버튼을 만들어 확인해본 결과 count값은 매 초 증가하는데 
updateTimer가 매 초 재 랜더링 되는 것이 아니기 때문에 타이머는 변함이 없었다.

의도 - 2

useCounter에 updateTimer 함수를 추가해서 Hook을 통해 타이머를 return 하려고 했다.

이 방법이 updateTimer 함수를 추가로 작성할 필요가 없어서 좋은 방법이라 생각했다.

오류 - 2

타이머를 나타내는 state의 변화가 없었다.
import React, { useCallback, useRef, useState } from 'react';

const useCounter = (initialValue, ms) => {
    const [count, setCount] = useState(initialValue);
    const [currentHours, setCurrentHours] = useState(0);
    const [currentMinutes, setCurrentMinutes] = useState(0);
    const [currentSeconds, setCurrentSeconds] = useState(0);
    const intervalRef = useRef(null);
    const start = useCallback(() => {
        if (intervalRef.current !== null) {
            return;
        }
        intervalRef.current = setInterval(() => {
            setCount(c => c + 1);
        }, ms);
    }, []);
    const stop = useCallback(() => {
        if (intervalRef.current === null) {
            return;
        }
        clearInterval(intervalRef.current);
        intervalRef.current = null;
    }, []);
    const reset = useCallback(() => {
        setCount(0);
    }, []);
    const timer = useCallback(() => {
        const checkMinutes = Math.floor(count / 60);
        const hours = Math.floor(count / 3600);
        const minutes = checkMinutes % 60;
        const seconds = count % 60;
        return (
            setCurrentHours(hours),
            setCurrentSeconds(seconds),
            setCurrentMinutes(minutes)
        )
    }, [])

    return { count, start, stop, reset, currentHours, currentMinutes, currentSeconds };
}

export default function SetTimer() {
    const { count, start, stop, reset, currentHours, currentMinutes, currentSeconds } = useCounter(0, 1000);

    return (
        <>
            <h1>{currentHours < 10 ? `0${currentHours}` : currentHours}:{
                currentMinutes < 10 ? `0${currentMinutes}` : currentMinutes}:{
                    currentSeconds < 10 ? `0${currentSeconds}` : currentSeconds}</h1>
            count = {count}
            <button onClick={start}>시작</button>
            <button onClick={stop}>정지</button>
            <button onClick={reset}>초기화</button>

        </>
    )
}

결과 - 2

count 자체가 매 초 변경되고 있으니 updateTimer는 useEffect를 활용하면 될 것이라는 생각이 떠올랐다.

해결

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

//사용자 정의 Hook
const useCounter = (initialValue, ms) => {
    const [count, setCount] = useState(initialValue);
    const intervalRef = useRef(null);
    const start = useCallback(() => {
        if (intervalRef.current !== null) {
            return;
        }
        intervalRef.current = setInterval(() => {
            setCount(c => c + 1);
        }, ms);
    }, []);
    const stop = useCallback(() => {
        if (intervalRef.current === null) {
            return;
        }
        clearInterval(intervalRef.current);
        intervalRef.current = null;
    }, []);
    const reset = useCallback(() => {
        setCount(0);
        stop()
    }, []);
    return { count, start, stop, reset };
}

export default function SetTimer() {
	//시, 분, 초를 state로 저장
    const [currentHours, setCurrentHours] = useState(0);
    const [currentMinutes, setCurrentMinutes] = useState(0);
    const [currentSeconds, setCurrentSeconds] = useState(0);
    const { count, start, stop, reset } = useCounter(0, 1000);

	// 타이머 기능
    const timer = () => {
        const checkMinutes = Math.floor(count / 60);
        const hours = Math.floor(count / 3600);
        const minutes = checkMinutes % 60;
        const seconds = count % 60;
        setCurrentHours(hours)
        setCurrentSeconds(seconds)
        setCurrentMinutes(minutes)
    }
    
    // count의 변화에 따라 timer 함수 랜더링
    useEffect(timer, [count]);
    return (
        <>
            <h1>{currentHours < 10 ? `0${currentHours}` : currentHours}:{
                currentMinutes < 10 ? `0${currentMinutes}` : currentMinutes}:{
                    currentSeconds < 10 ? `0${currentSeconds}` : currentSeconds}</h1>
            <button onClick={start}>Start</button>
            <button onClick={stop}>Stop</button>
            <button onClick={reset}>Reset</button>
        </>
    )
}

느낀 점

setInterval 사용에 집착하고 있어서 다른 기능을 생각하지 못했던 것 같다.
 
아직 React 기능의 활용보다는 Vanilla Js의 기능이 익숙한 것 같다.

역시 디버깅에 짜릿함은 디버깅 시간과 비례하는 것 같다.

앞으로의 계획

  • 약간의 CSS
  • 서버와 DB 연결을 통해 CRUD 기능 구현

참고자료

https://sangcho.tistory.com/entry/ReactHooks%EC%9D%98%EB%B9%99%EC%82%B0

 

React Hooks의 커다란 빙산

* 이 글은 Iceberg of React Hooks 번역하였습니다. The Iceberg of React Hooks React Hooks, unlike Class Components, provide low-level building blocks for optimizing and composing applications with m..

sangcho.tistory.com

https://ko.reactjs.org/docs/hooks-intro.html

 

Hook의 개요 – React

A JavaScript library for building user interfaces

ko.reactjs.org

 

반응형