useState() 사용 시 주의할 점

김세현·2022년 3월 2일
1

React

목록 보기
3/10

자바스크립트의 동기와 비동기의 간략한 설명


자바스크립트의 코드는 일반적으로는 동기식(synchronous)으로 처리된다.

즉 코드가 작성된 순서대로 윗줄부터 차례로 코드가 실행된다.

그래서 아래와 같은 코드는 순서대로 처리되어 출력 결과는 1, 2, 3 이 나오게 된다.

console.log('1');
console.log('2');
console.log('3');

그런데, 자바스크립트는 특정 함수들을 사용하게 되면 이를 비동기적(asynchronous)으로 처리한다.

만약 비동기 작업을 수행하는 코드를 만난다면, 이 코드의 출력 결과를 기다리지 않고 바로 다음 코드를 실행한다는 것이다.

예를 들면, 아래의 코드의 fetch는 비동기식으로 동작하는 API이다.

console.log('1');
fetch(url).then(data=>console.log('2'));
console.log('3');

처음으로 1을 출력하고, 바로 2가 출력되는 것이 아니라 fetch는 비동기적으로 처리되는 코드이기 때문

에 일단, 이를 어딘가에 따로 보관해두고 그다음 코드인 3을 출력한다.

그리고 나서 마지막으로 2가 출력된다.

자바스크립트의 동작에 대한 MDN 설명


React의 상태변경 함수의 특징 : asynchronous


두 가지 상태가 있다고 가정해보자
1. 버튼을 누른 횟수를 저장하는 count 상태
2. 같은 버튼을 누를때마다 키가 1cm씩 증가하는 height 상태


const [count, setCount] = useState(0);
const [height, setHeight] = useState(173);

function handleClick() {
  setCount(count + 1);
  setHeight(height+1);
}

버튼을 누를 때마다 당연히 둘이 똑같이 1씩 증가하는 코드이다.

그런데 사실, 위의 setCount(), setHeight() 같은 state 변경 함수들은 전부 asynchronous (비동기적) 으로 처리된다.

즉 위에서 비동기식 처리에 대해 설명했듯이 setCount(), setHeight()를 만나면 다음 코드부
터 실행한다는 것이다.

그렇기 때문에 상태 변경 함수를 동기식으로 처리된다고 인식하고 사용한다면 특정한 상황에서는 예상치 못한 버그가 생겨날 수 있다.

예를 들어 카운트가 3 이상이 된다면, height는 더 이상 증가하지 않도록 코드를 작성한다면, 아래와 같은 코드가 될 것이다.

    function handleClick() {
        setCount(count + 1); // 누를 때마다 count는 1씩 증가한다. 1 2 3 ...
        if (count < 3) { 
            setHeight(height + 1); //174 175 이후 height의 값은 175에서 변경되지 않아야 한다.
        }
    }

작성한 코드대로 라면,

count가 1, 2, 3이 된다면, if 조건문에 따라서

height는 174, 175가 되고, 더 이상 증가하지 않고 175에서 멈춰야 한다.

그런데 count가 3이 됐을 때, hegiht는 176까지 증가하게 된다.

분명 count가 2일 때까지만 height +1을 수행하라는 코드이다.

count가 1일 때 height +1 (=174)

count가 2일 때 hegiht +1 (=175)

count가 3이면 height +1 는 더 이상 수행하지 않기.

그런데 count가 3일 때도 height +1를 해주고 있다.

이유는 위에서 언급한 비동기적인 특성 때문이다.

상태 변경 함수는 비동기적으로 처리되는 함수기 때문에 해당 함수를 만나면 잠깐 제쳐두고 다음 코드를 실행한다.

그래서 코드를 해석해 보자면

① 버튼을 세 번째 누르면 setCount(count+1); 이걸 실행해서 count를 3을 만들어준다.

② 근데 count를 3으로 만드는 건 비동기 코드니깐 잠깐 제쳐두고 if ( count > 3 ) {} 이걸 실행하게 된다.

③ 이때 count는 아직 2라서 if문 안의 setHeight(height+1)이 잘 동작하고 있는 것이다.

즉 setCount()가 비동기적으로 처리되는 함수라서 그렇다.


해결 방법


위의 코드를 예상대로 동기적으로, 순차적으로 실행하고 싶을 때 해결책은 useEffect이다.

또한, 해당 컴포넌트가 처음 렌더링 된 것인지 아닌지의 상태도 필요하다.

(사실, count 상태만으로도 해결이 가능하긴 하다. 이 코드에서 count가 0일 때는 처음 렌더링 된 것을 의미하니까!)


useEffect(()=>{
  
  if(카운트가 3보다 작을 때 && 해당 컴포넌트가 처음으로 렌더링된 것이 아닐때){
     setHeight(hegiht+1);
  }

  //...

},[count]) //count가 변경될 때마다 실행

function handleClick() {
  setCount(count + 1);
}


추가 : 이전 상태에 의존하는 상태를 변경하는 방법


function handleClick() {
	setCount(prevCount => prevCount+1);
}
profile
under the hood

0개의 댓글