[TIL] React | setState 비동기 처리하기 🐣

dosilv·2021년 5월 13일
3
post-thumbnail

🐣 동기와 비동기

복수의 프로세스가 존재할 때,
동기(synchronous)적 처리는 하나씩 순차적으로, 이전 작업이 끝날 때까지 기다렸다가 처리하는 것
비동기(asynchronous)적 처리는 전 작업이 끝나지 않아도 다음 작업으로 넘어가서 동시에 처리하는 것

JavaScript는 기본적으로 동기적 언어이다. 코드가 윗줄부터 아랫줄로 내려오며 실행되기 때문! 하지만 효율성을 높이기 위해서 비동기적으로 처리되는 일부 연산이 존재한다. setTimeout, ajax와 fetch, setState 등... 그리고 그 해결책으로 callback, Promise, async&await 같은 것들이 사용되고 있다.

프로젝트 도중에 비동기 함수인 setState를 동기적으로 처리해야 하는 난관이 있었다. previousState(updater 함수)를 통해 해결할 수 있었는데... 바쁘지만 까먹기 전에 정리해 두기~~~

⚠ '비동기처리'라는 용어가 처음에 디게 혼란을 줬는데... '비동기적으로 처리'가 아니라 '원래 비동기인 작업을 동기적인 방식으로 처리'에 가까운 것 같음!



🐥 setState는 비동기 함수!

setState가 비동기 연산이라는 의미를 정확히 짚어 보자.

this.state = { number: 0, nextNumber: 0, nextNextNumber: 0 }

이런 state값들이 있을 때, setState를 사용해 number에 +1을 하고, nextNumber는 number에 +1, nextNextNumber는 nextNumber에 +1을 한 값으로 설정한다고 하면,

this.setState({ number: number + 1 });
this.setState({ nextNumber: number + 1 });
this.setState({ nextNextNumber: nextNumber + 1 });

이렇게 표현할 수 있다.

그런데! 그 다음 줄에 콘솔로그로 number, nextNumber, nextNextNumber를 찍어 보면 첫 실행 후는 0, 0, 0, 두 번째 실행 후는 1, 1, 1, 세 번째 실행 후는 2, 2, 2... 이렇게 나온다.

만약 동기적으로 처리가 됐다면 1, 2, 3, 2, 3, 4, 3, 4, 5... 순서로 나와야 하는데! 저 세 줄이(+그 아래 console.log까지) 동시에(즉 비동기적으로) 처리되기 때문에 저런 결과가 나오는 것...! 😕



🐤 setState를 동기적으로 실행시키기

그럼 어떻게 하느냐! 두 가지 방법이 있다.

1. 콜백함수 이용하기

setState는 사실 두 개의 파라미터를 받을 수 있다!
(1) 객체(object) 또는 함수
(2) 콜백함수

여기서 (1)의 객체가 우리가 보통 setState로 일부 state값을 변경하는 방식이고, 함수는 2번에서 설명할 예정! (2)의 콜백함수가 지금 설명할 첫 번째 방법이다.

그냥 두 번째 인자로, setState로 값을 변경한 뒤에 실행시키고 싶은 함수를 넣으면 된다. 위의 예시를 콜백함수로 처리한다고 치면,

this.setState({ number: this.state.number + 1 }, () => {
  this.setState({ nextNumber: this.state.number + 1 }, () => {
    this.setState({ nextNextNumber: this.state.nextNumber + 1 }, () => {
      console.log(
        this.state.number,
        this.state.nextNumber,
        this.state.nextNextNumber
      );
    });
  });
});

이렇게!

한 번 쓰기에는 간단하지만, 비동기 처리해야 할 함수가 많아지면 끝도 없는 depth가 발생한다. 이게 바로 callback hell.... 그래서 추천하지 않는 방법.

그리고 테스트하다 발견한 건데, callback함수 형태로 비동기를 처리할 경우 this.state를 비구조화할당하면 제대로 처리가 안 된다. 꼭 풀네임으로 써줘야 함... 🤨


2. updater(previousState) 이용하기

그래서 선택한 두 번째 방법..! 위에서 첫 번째 인자로 객체 말고도 함수를 전달할 수 있다고 했는데, 그 함수를 updater라고 한다.

updater는 인자로 previousState를 받는데(이름은 상관 ❌), updater의 return값으로 원래 setState에 넣을 객체를 넣되, 동기처리가 필요한 state값(변수) 앞에 previousState를 붙여 주면 그전 setState에서 변경된 state값이 반영된다! 설명이 무척... 더 어려운 것 같은데..... 아래 예시를 보면 알 수 있을 것...ㅎ.ㅎ

this.setState({ number: number + 1 });
this.setState(previousState => ({ nextNumber: previousState.number + 1 }));
this.setState(previousState => ({ nextNextNumber: previousState.nextNumber + 1 }));

처음 setState는 어차피 젤 먼 시작하니까 그냥 객체를 인자로 주고, 두 번째와 세 번째 setState는 각각 이전의 setState 결과가 반영될 수 있도록 updater 함수 형식으로 작성했다. 콘솔창을 확인하면 리렌더링 될 때마다 0, 0, 0, 1, 2, 3, 2, 3, 4, 3, 4, 5... 이렇게 동기적으로 실행되는 걸 알 수 있다.

만약 뒤에 올 console.log도 동기적으로 처리하고 싶다면

this.setState(prevState => {
  console.log(prevState.number, prevState.nextNumber, prevState.nextNextNumber);
});

이런 식으로 작성해 주면 아쥬 잘됨! 🥳🥳



🙇‍♀️ 참고한 자료

비동기로 작동하는 setState 이해하기
[React]setState 비동기 동작 | by ych Dev | Medium

profile
DevelOpErUN 성장일기🌈

0개의 댓글