React

setState 동기로 처리해주기

삐옹 2022. 7. 14. 01:45

자바스크립트는 기본적으로 동기적인 언어다.

자바스크립트 엔진, 그러니까 코드를 읽을 때 위에서부터 아래로

한 줄 씩 읽으며 처리한다는 뜻이다.

 

여기서 일을 처리하는데 비효율성이 생겨났다.

그걸 해결해주기위해 ajax, fetch, setTimeOut 같은 것들이 나타났다.

react에서는 setState가 그렇고..

 

이번 파이널 프로젝트에서 실시간 채팅 기능을 넣기로 했다.

어떤 값을 setState로 업데이트 하면서 바로 그 다음 줄에 함수를 동시에 실행시켰어야 했다.

포인트는 업데이트된 값을 인자로 받는 함수였다는 것.

보기좋게 에러를 띄웠다..

인자로 오는 값이 빈값인걸 보니 얼핏 들었던 setState의 비동기적 특성 때문에 일어난 것임을 짐작했다.

 

상황 1.  setState()에 의해 업데이트된 값을 이용해 바로 다음 줄에 함수 실행하기

  const [name, setName] = useState("");
  ..
  setName("Venny")
  ..

setState는 상태값을 업데이트시킵니다.

근데 문제는 setName()같은 state 변경함수는 전부 비동기적으로 처리됩니다.

다시말해서 setName()이 오래 시간이 걸리면 이걸 제껴두고 다른 코드부터 실행한다는 것입니다.

이때 예상치 못한 문제를 마주칠 수 있습니다.

 

const [name, setName] = useState("");

  function update(){
    setName("Venny")
    handleSubmit(name)
  }
  
  function handleSomething(name){
    // axios.post()...
  }

이런 경우 업데이트 되지 않은 name의 값("")이 handleSomething 함수로 들어가면서

axios요청에 에러가 생기게 된다.

이를 해결하기 위해선 name이 완전히 업데이트가 완료된 후

handleSomething을 실행해주면 된다.(동기적 처리)

 

이러한 과정을 '비동기처리'라고 부릅니다. 이름이 헷갈려서 그동안 오해를 하고 있었는데, 이 말은 '비동기적으로 처리'가 아니라
'비동기를 동기로 처리'에 가깝습니다.😨

 

위와 같은 상황에서 setState를 동기적으로 처리하는 방법을 정리해본다.

 

해결. useEffect 활용하기

순차적으로 setName()이 완전히 실행된 뒤에 

무언가를 실행시키고 싶다면 

의존성 배열과 함께 useEffect를 활용하면 된다.

의존성 배열 안에 넣은 데이터가 업데이트 된 후

useEffect 안의 함수를 실행시키겠다는 원리를

생각해보자.

 

useEffect 의존성 배열에 대한 내용들은 검색하면 많이 나오니 생략합니다~

 

  const [name, setName] = useState("");
  function update(){
    setName("Venny")
  }
  
  function handleSomething(name){
    // axios.post()...
  }
  
  useEffect(() => {
  	handleSomething(name);
  },[name]) // name이 업데이트 된 후 useEffect 콜백함수를 실행한다.

 

상황2. setState() 여러개 쓰기

좀 다른 상황이지만 이런 경우도 있을 수 있단걸 블로그 서칭 중 보아서 정리해본다

 

setState는 두 개의 파라미터를 받을 수 있다.
(1) 객체 또는 함수
(2) 콜백 함수
const [state, setState] = useState({ number: 0, nextNumber: 0, nextNextNumber: 0 })


const handleClick = () => {
	setState(number + 1);
	setState(nextNumber: number + 1);
	setState(nextNextNumber : nextNumber + 1);
}

return ..

버튼을 클릭했을 때 세 개의 값을 업데이트 시켜야한다면 어떻게 될까.

앞에 업데이트된 값에서 1씩 더해주는 로직이다.

 

콘솔을 찍어보면 첫 번 쨰 실행에는 1,2,3, 두 번쨰 실행에는 2,3,4, 세 번쨰 시행에는 3,4,5 라고 들어올 것 같지만

땡 틀렸다

1 ,1 ,1 / 2 ,2 ,2 / 3 ,3 ,3 이렇게 나온다..

처음 number + 1 은 잘 실행됬지만, 중요한건 그 실행을 기다리는 중에 이미 다음 number는 이미 0으로 계산되서 들어갔기 때문이다.

(비동기!!)

 

해결. updater

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

위에서 첫 번째 인자로 객체 또는 함수를 전달 할 수 있다고 했는데 여기서 함수가 바로 previousState 이다.

그 함수의 정식명칭은 updater라고 한다.

updater는 인자로 previousState를 인자로 받는데(이름 아무거나 괜춘), updater의 return 값으로 원래 업데이트 하려던 값(객체)를 넣되, 동기로 처리해야하는 state값 앞에 써줄 경우 그 전 setState에서 변경된 state의 값이 반영된다. 

 

이렇게 하면 1, 2, 3 / 2, 3, 4, / 3 ,4 ,5 ... 가 잘 나온다.