만족

[React] 반복문 속 setState를 사용할 때의 주의점 본문

[React] 반복문 속 setState를 사용할 때의 주의점

FrontEnd/React Satisfaction 2020. 3. 23. 22:15

React component 자신이 가진 상태값은 state라고 하고, setState를 이용해서 그 상태값을 변화시킨다.

 

그런데, this.setState()는 state를 비동기적으로 변화시킨다.

 

즉, this.setState() 라인이 호출되고 다음 라인으로 넘어갔을 때, this.state의 값은 바뀌었을 수도, 바뀌지 않았을 수도 있다는 말이다.

 

위의 코드를 보면, 실행되고 난 다음에는 this.state.value의 값이 101로 찍힐 것 같지만 실제로는 2가 나온다.

(물론 다른 값이 나올 수도 있다)

 

왜 그럴까?

 

setState()는 즉각적으로 상태를 업데이트하지 않고, Promise를 이용해 비동기적으로 상태를 변화시키기 때문이다.

(Promise에 대해서 잘 모른다면, 다음 글을 참고하면 좋다 https://satisfactoryplace.tistory.com/84)

 

setState()를 호출하면 UpdateQueue에 업데이트 요청이 큐잉되고, 비동기적으로 하나씩 처리하는데,

이 업데이트 요청이 완료되기 전에 새로운 업데이트 요청이 들어오게 되면,

업데이트 전의 값을 기준으로 다시 큐잉되게 된다.

정리하면 다음과 같다.

 

1. (l= 0) state.value(현재 값 1)를 1 증가시키라고 업데이트 요청

2. 업데이트 처리중

3. 아직 업데이트가 완료되지 않은 상태에서 (l=1) state.value(현재 값은 아직 1)를 1 증가시키라고 업데이트 요청

4. 업데이트 처리중

....

201. 아직 업데이트되지 않은 state.value(아직 업데이트 되지 않음)을 콘솔에 출력

 

이 상태에서 조금 기다린 다음에 state.value값을 찍어보면 2가 나온다.

왜냐면 state.value값은 201번째까지 실제로 업데이트되지 않았으므로,

100개의 업데이트 요청은 전부 'state.value를 1+1로 업데이트하라'는 요청이 되기 때문이다.

 

어떻게 이 문제를 회피할 수 있을까?

첫 번째는 setState가 Promise를 반환한다는 점에 착안하여 해결한다.

 

다음과 같이 await를 이용해 업데이트가 완료되기 전까지 코드 실행을 정지시키는(blocking) 방법이다.

 

그러나, 이 방법을 사용할 때는 조심해야 한다.

state가 바뀔 때마다 해당 컴포넌트와 하위 컴포넌트는 re-rendering을 수행하게 되는데

이 경우 100번의 re-rendering이 발생하기 때문에 사실 성능면에서는 좋은 방법은 아니기 때문이다.

 

따라서 state가 바뀌는 모습을 반드시 시각적으로 보여줄 필요가 없는 상태 변화라면

위의 방법은 별로 권장되지 않는다.

(물론 저 경우에는 너무 빨리지나가서 화면에 보이지도 않는다... 자원만 낭비할 뿐)

 

게다가 함수형 컴포넌트에서 useState로 생성한 setter는 promise를 리턴하지 않기 때문에 위 방법은 사용할 수 없다.

 

두 번째 방법은 value값이 업데이트되는게 실시간으로 rendering될 필요가 없으므로

그냥 변수 하나에다가 처리를 진행한 후, 최종 결과값만을 setState를 이용해 반영시키는 방법이다.

(당연하게도 위 반복문은 동기적으로 처리되기 때문에 추가 작업이 필요하지 않다)

 

물론 setState이후에 console.log(this.state.value)를 했을 때, 최종 결과값이 나온다는 보장은 없지만,

value와 this.state.value(기댓값)은 같기 때문에, 값이 필요한 추가 작업이 있다면 그냥 value를 사용하면 된다.



Comments