일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- til
- CORS
- 원티드
- 자바스크립트
- 브라우저
- v9
- react
- Reducer
- Frontend
- 타입스크립트
- Component
- TypeScript
- react localStorage
- es6
- 비트 연산자
- Redux
- 알고리즘
- 파이어베이스
- state
- 리액트
- axios
- 프론트엔드
- 프리온보딩
- JS
- localstorage
- array
- firebase
- 프로그래머스
- 컴포넌트
- JavaScript
- Today
- Total
도리쓰에러쓰
[React] state 변경함수 사용할 때 주의점 : async 본문
💡 2022년 이후 React 18.0ver batching 업데이트 나오면 async 몰라도 됩니다 !
1. JavaScript의 sync / async 관련 상식
JavaScript는 일반적인 코드를 작성하면 동기(Synchronous) 처리가 됩니다.
동기(Synchronous) 처리는 코드 윗줄부터 차례대로 코드가 실행된다는 뜻입니다.
아래 코드는 콘솔창에 2, 3, 4 순으로 출력되고 있습니다.
console.log(1+1); // 2
console.log(1+2); // 3
console.log(1+3); // 4
하지만 JavaScript는 비동기(Asynchronous) 하게 코드 실행이 가능한 함수들이 있습니다.
ajax, 이벤트리스너, setTimeout 이런 함수들을 쓸 때 비동기 처리가 일어납니다.
이런 함수들은 처리 시간이 오래 걸립니다. ajax로 예를 들면 인터넷 상황이 좋지 않으면 코드 실행이 오래 걸립니다.
그래서 ajax 요청하는 코드들은 순차적으로 실행되지 않고 완료되면 실행됩니다.
예를 들어 아래 코드는 2, 4, 3 순으로 출력되고 있습니다.
console.log(1+1);
axios로 get 요청하고 나서 console.log(1+2); 실행
console.log(1+3);
3을 출력하는 코드가 비동기(Asynchronous) 처리를 지원하는 코드이기 때문입니다.
3을 출력할 때 오래 걸리면 완료될 때까지 잠깐 보류했다가 다른 코드를 먼저 실행시키는 것입니다.
심지어 ajax 요청이 0.00초 걸려도 2, 4, 3 순으로 출력이 됩니다. 물리적으로 잠깐 처리가 보류됐기 때문입니다.
2. React의 setState 함수 특징
React로 state를 만들 땐 아래 코드처럼 작성합니다.
function App() {
let [name, setName] = useState('kim');
}
setName()을 사용하면 name이라는 state를 자유롭게 변경할 수 있습니다.
근데 setName() 같은 state 변경 함수들은 전부 비동기(Asynchronous)로 처리됩니다.
setName()이 오래 걸리면 이건 보류되고 밑에 있는 코드들부터 실행된다는 겁니다.
그래서 뭔가 예상치 못한 문제가 생길 수 있습니다.
아래 코드에서 버튼을 클릭하면 2개의 기능을 순차적으로 실행하려고 합니다.
function App() {
let [count, setCount] = useState(0);
let [age, setAge] = useState(20);
return (
<div>
<div>안녕하세요 {age}살입니다.</div>
<button>누르면 한살 추가</button>
</div>
)
}
버튼을 클릭할 때 아래 기능을 구현하려고 합니다.
1️⃣ count라는 state를 +1 해야합니다. ( 버튼 누른 횟수 기록용 )
2️⃣ age라는 state도 +1 해야합니다.
3️⃣ 근데 count가 3 이상이면 더 이상 age라는 state를 +1 하지 않도록 해야합니다.
아래 코드처럼 button에 onClick 이벤트리스너를 작성하였습니다.
function App() {
let [count, setCount] = useState(0);
let [age, setAge] = useState(20);
return (
<div>
<div>안녕하세요 {age}살입니다.</div>
<button onClick={ () => {
setCount(count+1);
if(count < 3) {
setAge(age+1);
}
} }>누르면 한살 추가</button>
</div>
)
}
1️⃣ 버튼을 클릭하면 count를 +1 해줍니다.
2️⃣ 그리고 count라는 state가 3보다 적으면 age를 +1 해줍니다.
3️⃣ count가 2일 때까지 setAge()가 실행되니까 age는 22가 나오겠네요.
근데 age는 23까지 증가하고 있습니다.
count가 3일 때도 age + 1을 해주고 있는 것 같습니다.
이유는 async라는 특징 때문에 그렇습니다.
state 변경함수는 async하게 처리되는 함수이기 때문에 완료되기까지 시간이 오래걸리면 보류하고 다음 코드를 실행해줍니다.
그래서 코드를 해석해보자면,
1️⃣ 버튼을 3번째로 누르면 setCount(count+1); 를 실행해서 count를 3으로 만들어줍니다.
2️⃣ 근데 count를 3으로 만드는건 오래걸리니까 보류하고 if ( count < 3 ) {} 을 실행합니다.
3️⃣ 이 때 count는 아직 2라서 if문 안의 setAge(age+1); 이 잘 동작하고 있는겁니다.
그래서 state 1 변경하고 나서 state 2를 변경하는 코드를 작성할 땐 가끔 문제가 생깁니다.
이걸 순차적으로 실행하고 싶을 때 useEffect()를 사용하면 됩니다.
* useEffect : Component가 렌더링/재렌더링될 때 실행되는 함수입니다.
함수 뒤 [] 대괄호 안에 state를 넣으면 state가 변경되면 useEffect()가 실행되는 겁니다.
function App() {
let [count, setCount] = useState(0);
let [age, setAge] = useState(20);
useEffect( () => {
if( count != 0 && count < 3 ) {
setAge(age+1);
}
}, [count])
return (
<div>
<div>안녕하세요 {age}살입니다.</div>
<button onClick={ () => {
setCount(count+1);
} }>누르면 한살 추가</button>
</div>
)
}
1️⃣ 버튼을 클릭하면 count가 +1 됩니다.
2️⃣ count라는 state가 변경되면서 useEffect()가 실행됩니다.
3️⃣ count가 0이 아니거나 3보다 적을 때 age가 +1 됩니다.
💡 useEffect()는 처음 페이지 로드될 때도 한번 실행이 되기 때문에, 처음 페이지 로드 시 useEffect() 실행을 막는 코드를 적용하셔도 되고, 저처럼 if 조건문에 count != 0을 작성하셔서 페이지 로드될 때 count는 0이므로 내부 코드가 동작되지 않도록 작성해주셔도 됩니다.
이 방법이 아니여도,
1️⃣ count와 age를 동시에 한 곳의 state에 array 혹은 object 자료형으로 넣어도 해결 가능할 것 같습니다.
2️⃣ 하나는 state로 만들지 않고 let 변수로 만드는 것도 괜찮을 것 같습니다. (모든걸 state로 만들 필요는 없습니다. 바뀌면 HTML 재렌더링이 필요한 변수들을 state로 만들면 됩니다. 필요없는건 let 변수로 만들어도 됩니다.)
'코딩애플 (React) > 기초수업(코딩애플) - 3' 카테고리의 다른 글
[React] 성능잡기2 :: 쓸데없는 재렌더링을 막는 memo (0) | 2022.02.02 |
---|---|
[React] 성능잡기1 :: lazy loading / React devtools (0) | 2022.02.02 |
[React] React에서 자주 사용하는 if문 작성패턴 5개 (0) | 2022.02.02 |
[React] shop 홈페이지 기능 완성하기 가이드 (0) | 2022.02.01 |
[React] Redux5 :: useSelector, useDispatch (0) | 2022.02.01 |