react 로 서비스를 구현하면서 setState 의 비동기성 때문에 많은 어려움을 겪었습니다.
jquery 에 익숙해서 리액트스럽지 않은 코드 스타일이여서 그런지 구글링해도 쉽게 찾을 수 없어 정리해보려고 합니다.(결국 구글링한 내용입니다.)
list 에서 첫번째 list 를 뽑아서 first에 넣고 다시한번 더 뽑아서 second에 넣는 코드를 생각해봅시다.
1 2 3 4 5 6 7 |
const [list, setList] = useState(null); const [first, setFirst] = useState(null); const [second, setSecond] = useState(null); setList([1,2,3]); setFirst(list.shift(0)); //Cannot read properties of undefined (reading 'shift') setSecond(list.shift(0)); |
리액트의 function 컴포넌트에서 hook 중 하나인 useState는 비동기적으로 실행되어 리랜던링이 된 후에 state 값을 update 하기 때문에 위의 코드는 실행시 에러가 발생합니다.
react에서는 비동기성을 제어하기 위해 useEffect 를 사용하라고 가이드 합니다.
위의 코드를 해결하기 위해서는 금방 구글링하면 결과를 찾을 수 있습니다.
1 2 3 4 5 6 7 8 |
const [list, setList] = useState(null); const [first, setFirst] = useState(null); const [second, setSecond] = useState(null); useEffect(()=>{ setFirst(list.shift(0)); setSecond(list.shift(0)); },list); setList([1,2,3]); |
코드를 만들다보면 callback 처리 없이 바로 list의 값을 제어하고 싶을때도 많이 있습니다.
그럼 그때마다 useEffect 함수 속에서 로직을 수행해야하는 불편함이 머리속을 떠나지 않습니다.
그래서 첫번째 코드를 실행할 수 있는 방법이 계속 궁금했습니다.
결론적으로 function 컴포넌트에서는 위의 코드를 실행할 수 있는 방법은 없습니다.
리액트의 setSate의 경우 closure 로 useState의 두번째 return 값인 set 함수를 return 하는데 closure 특성상 현재 랜더링 된 state(list=null)를 보기 때문에 리랜더링 이후 list 의 set 함수가 호출되기 전까지는 계속 null 입니다.
Promise를 사용하여 체이닝 하여 순서를 제어할 수 있다고 합니다만 여간 까다로운게 아니네요.
Jquery 기준으로 보면 DOM이 렌더링 되는 순서에 따라 event 를 생성해서 로직의 실행 순서를 제어했으나, react의 경우 react의 lifecycle 을 이해하고 state 의 변화를 제어해야하는 것이 번거러워 보입니다.
이런 불편함을 저만 느낀건 아닌것 같습니다.
구글링해서 찾은 어떤 블로그는 이런 불편함 때문에 Mobx 라는 라이브러리를 사용해서 local 변수로 관리한다고 합니다.
또 Mobx라는 라이브러라도 공부해야하는군요.
React의 많은 장점이 있겠지만 React가 가진 단점을 커버하기 위한 너무 많은 추가적인 컴포넌트를 학습해야하는게 더 큰 단점으로 다가옵니다.
참고 : https://zigsong.github.io/2021/10/16/fe-await-setstate/
https://simsimjae.tistory.com/448