React Counter 컴포넌트 구현

raccoonback·2020년 7월 21일
1

React

목록 보기
1/1
post-thumbnail

Counter 컴포넌트를 구현하는 과정에서 알게된 부분을 기록한 글입니다.

구현하고자 했던 기능은 1초마다 1씩 증가하는 Counter 컴포넌트였다.
그래서 아래 코드와 같이, setCounter() 함수를 이용해서 1초마다 counter 상태를 수정하도록 setInterval() 함수를 호출하였다.

import React, { useState } from 'react';

function Counter () {
    const [counter, setCounter] = useState(0);

    setInterval(() => { setCounter(counter + 1);}, 1000);

    return (<div>{counter}</div>);
}

export default Counter;

하지만, 위 코드는 중간에 0부터 다시 시작하는 등의 예상치 못한 동작을 하게 된다.

위 코드는 크게 두 가지 문제가 있는데 하나씩 살펴보자.

중첩 호출

우선, 중간에 Count가 0부터 다시 시작한다는 것은 setInterval() 함수가 중첩되서 그런건 아닐까?

맞다.
따라서 이를 해결하기 위해서는 useEffect() Hook 함수를 사용하면 된다.
useEffect() Hook 함수의 두 번째 인자로 빈 배열([])을 전달하면, 어떤 값에도 의존하지 않으며 재실행될 필요가 전혀없기 때문에 첫 렌더링시에만 호출되도록 보장한다.
즉, useEffect(effect, [])를 이용하면 setInterval()가 오직 첫 번째 렌더링에서만 호출되도록 보장한다.

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

function Counter () {
    const [counter, setCounter] = useState(0);

    useEffect(() => {setInterval(() => { setCounter(counter + 1);}, 1000);}, []);

    return (<div>{counter}</div>);
}

export default Counter;

참고자료

그런데 setInterval()가 오직 한 번만 호출되도록 보장은 하였지만, setInterval()에 전달한 콜백 함수가 한 번만 호출되고 이후에는 멈춘다...;

비동기적인 상태 변경

setInterval()에 전달한 콜백 함수가 한 번만 호출되는 것일까?

그 이유는 setInterval() 함수가 비동기 함수이기 때문이다.
즉, setInterval(callback, 1000)는 비동기적으로 1초마다 callback()을 호출하는데, State 업데이트는 비동기적으로 처리하지 않아서 그런 것이다.

따라서 비동기적으로 업데이트해야 하는 경우에는 수정 메서드에 비동기 처리가 완료된 시점에 호출할 함수를 인자로 전달한다.

function cb(prev) {
  return prev + 1;
}

즉 위와 같이 함수를 수정 메서드(setCounter())의 인자로 전달하면, 비동기 처리가 완료된 시점에 cb(이전 state 정보) 함수를 호출한다.

const [counter, setCounter] = useState(0);

setInterval(() => { setCounter(cb); }, 1000);

참고자료

결론

결과적으로, 위 두 가지를 모두 적용해보면 아래와 같다.

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

function Counter () {
    const [counter, setCounter] = useState(0);
    
    function cb(prev) {
        return prev + 1;
    }

    function effect() {
        setInterval(() => { setCounter(cb); }, 1000);
    }
    
    useEffect(effect, []);

    return (<div>{counter}</div>);
}

export default Counter;

즉, 1초마다 정상적으로 카운팅되는 것을 확인할 수 있다.

profile
한번도 실수하지 않은 사람은, 한번도 새로운 것을 시도하지 않은 사람이다.

0개의 댓글