[React]setState 사용할 때 주의점(feat.async)

Yeongsan Son·2021년 5월 29일
0

JS 동기와 비동기

자바스크립트 언어는 코드를 한 줄로 읽어 나가는 대표적인 인터프리터 언어이다.
이 말은 순차적으로 즉 동기적으로 코드가 실행된다는 의미이다.
(동기와 비동기라는 말이 참 와닿지 않는 말이긴 하지만...)
하지만, 인간의 욕심은 끝이 없고 우리는 어떤 코드를 실행하는데 시간이 걸린다면 이 다음 코드가 먼저 실행되길 바랍니다. 이에 비동기라는 기술이 나와서 저와 같은 코린이들에게 많은 어려움을 선사합니다.

  • 동기 : 위에서부터 2, 3, 4가 콘솔에 잘 찍힙니다.
console.log(1+1)
console.log(1+2)
console.log(1+3)
  • 비동기: 대표적으로 서버 통신에 사용되는 Ajax 요청, setTimeout, 이벤트리스너에서 많이 사용됩니다
console.log(1 + 1); // 1
axios.get('/data', (req, res) => {
  console.log(1 + 2); // 2
})
console.log(1 + 3); // 3

이 코드에서는 1번 -> 3번 -> 2번 순으로 실행됩니다.
Ajax요청을 보내는 axios 코드는 비동기로 처리되기 때문에 3번의 코드가 먼저 실행되고 Ajax 요청이 완료되면 2번의 코드가 실행됩니다.

많은 곳에서 비동기를 설명할 때, 카페 예시를 많이 가져다 씁니다.

동기적으로 주문을 받는 카페는 하나의 주문을 받고 그 주문에 대한 음료를 제공하고 다음 주문을 받기 때문에 문제점이 발생한다는 것이고,

비동기적으로 주문을 받는 카페는 하나의 주문을 받고 그 주문에 대한 음료를 만들면서 다음 주문을 받는다라는 예시입니다.

고객의 입장에서는 당연히 비동기 방식이 좋아 보입니다.

하지만 카페의 입장에서는 인건비가 더 발생하기 때문에 어떤 방법이 더 많은 매출이 발생하는지 계산해 볼 필요가 있습니다.

이와 마찬가지로 우리가 프로그램을 짤 때에도 어느 방식이 효율적인지 고려하고 사용해야 합니다.

리액트의 setState 함수 특징

리액트에서는 state를 다음과 같이 생성합니다.

function App(){
  let [name, setName] = useState('Sonny')
}

useState 함수는 name 이라는 변수에 Sonny 라는 초기값을 할당해 줍니다.

setName이라는 변수에는 어떤 마법으로 name이라는 변수의 값을 변경할 수 있는 제어권을 갖게 됩니다.

현재 이름을 Sonny 에서 Son으로 변경하고 싶다면

setName('Son')이라는 코드로 이름을 변경해 줄 수 있습니다.

그런데 setName과 같은 state를 변경하는 함수들은 비동기적으로 처리가 됩니다.

setName()이 오래걸리면 이거 제껴두고 다른 밑에 있는 코드들부터 실행한다는 겁니다.

그래서 뭔가 예상치 못한 문제가 생길 수 있습니다.

  • 예제 : 버튼을 누르면 2개 기능을 순차적으로 실행
function App(){
  let [count, setCount] = useState(0);
  let [age, setAge] = useState(31);

  return (
    <div>
      <div>안녕하십니까 저는 {age}</div>
      <button>+1</button>
    </div>
  )
}

버튼을 누를 때마다

  • age라는 state를 +1 해야합니다.
  • count라는 state도 +1 해야합니다. 이거는 버튼누른 횟수 저장하는 곳입니다.
  • count 가 3 이상이면 setCount를 stop(더 이상 나이를 먹고 싶지 않다ㅇ아ㅏ...)

조건에 따라 다음과 같이 코드를 작성하면 이상한 점을 발견할 수 있습니다.

<button onClick={()=>{

  setCount(count+1);
  if ( count < 3 ) {
    setAge(age+1);
  }
         
}}>+1</button> 

age가 33에서 더 이상 증가하지 않고 멈출거라고 기대했지만 33까지 증가합니다..!

왜 이런 일이 발생할까요?

이유는 state 변경함수는 비동기 함수이기 때문에 완료되기까지 시간이 오래걸리면 제쳐두고 다음 코드를 실행합니다.

① 버튼을 세번째 누르면 setCount 함수를 실행해 count를 3을 만들어줍니다.
② 근데 count를 3으로 만드는건 오래걸리니까 다음 코드인 조건문을 실행합니다.
③ 이 때 count는 아직 2라서 if문 안의 age+1이 동작하게 됩니다.

이 문제를 동기적으로,즉 순차적으로 실행하고 싶다면 useEffect 함수를 사용하면 됩니다.

useEffect 함수는 특정 state가 변경될 때, 함수를 실행하도록 할 수 있습니다.


const App = () => {
  let [count, setCount] = useState(0);
  let [age, setAge] = useState(31);
  
  useEffect(()=>{
    if ( count < 3 ) {
      setAge(age+1)
    }
  }, [count]) 

  return (
    <div>
      <div>안녕하십니까 저는 {age}</div>
      <button onClick={() => setCount(count + 1)}>+1</button>
    </div>
  )
}

다음과 같이 코드를 작성한다면 useEffect 함수의 조건문은 count의 상태가 변경될 때만 실행됩니다.

하지만, useEffect 함수는 페이지 로드 시에 실행되기 때문에 의도치 않은 조건문 실행을 막으려면

조건문에 count !== 0 && count < 3 이라고 수정하면 원하던 결과물이 나오게 됩니다.

페이지 로드 시에 useEffect 함수가 실행되지만 count가 0이기 때문에

조건문에 만족하지 않아 setAge 함수가 실행되지 않습니다.

profile
매몰되지 않는 개발자가 되자

1개의 댓글

comment-user-thumbnail
2021년 9월 1일

링크없는 강의내용 무단복제 금지입니당

답글 달기