function Counter() {
const [count, setCount] = useState(0);
function handleAlertClick() {
setTimeout(() => {
alert('You clicked on: ' + count);
}, 3000);
}
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
<button onClick={handleAlertClick}>
Show alert
</button>
</div>
);
}
위의 코드는 간단하게 버튼이 두 개 있습니다. 하나의 버튼은 count 라는 상태값을 증가시켜주며, 또 다른 버튼은 count라는 상태값을 3초 뒤에 alert 해줍니다.
여기서 다음과 같이 행동을 해봅니다.
💡 카운터를 3으로 증가시킵니다. ► "Show alert"버튼을 누릅니다. ► 타임아웃(3초)가 지나기 전에 카운터를 5로 증가시킵니다.결과는 바로 3이 나옵니다. 왜냐하면 state는 리렌더링이 될 때 아예 새로운 함수를 반환해주기 때문입니다.
// 처음 랜더링 시
function Counter() {
const count = 0; // useState() 로부터 리턴
// ...
function handleAlertClick() {
setTimeout(() => {
alert('You clicked on: ' + count);
}, 3000);
}
// ...
}
// 클릭하면 함수가 다시 호출된다
function Counter() {
const count = 1; // useState() 로부터 리턴
// ...
function handleAlertClick() {
setTimeout(() => {
alert('You clicked on: ' + count);
}, 3000);
}
// ...
}
// 또 한번 클릭하면, 다시 함수가 호출된다
function Counter() {
const count = 2; // useState() 로부터 리턴
// ...
function handleAlertClick() {
setTimeout(() => {
alert('You clicked on: ' + count);
}, 3000);
}
// ...
}
이런 식으로 함수가 재생성이 되는 것..각각의 함수에서 count는 독립적인 것.. 따라서 count가 3일때 버튼을 눌렀다면, alert를 해주는 함수는 이 때의 상태값은 count == 3 을 기억하고 있을 것이며 그 것을 alert 해준다.. 이 것은 실제의 함수에서도 발생하는 상황입니다.
위 사진이 바로 컴포넌트가 없데이트 되어서 화면에 보이기까지의 순서를 담은 것 이다. 함수에서 proprs가 바뀌거나 state가 바뀌면 shouldComponentUpdate 과정이 진행됩니다.
setState함수는 비동기 함수이다. 1번에서 새로운 컴포넌트를 불러오는 과정에서 원래의 함수가 계속 진행이 되었던 것도 결국에는 비동기함수이기 때문에 가능했던 것입니다. 자 그럼 비동기 함수이기 때문에 발생하는 문제점이 있다.
import React, { useState } from "react";
function App() {
const initialNum = 0;
const [num, setNum] = useState(initialNum);
const IncrementByFive = () => {
for (let i = 0; i < 5; i++) {
setNum(num + 1);
console.log(num);
}
};
return (
<div>
<p>Counter: {num}</p>
<button onClick={() => setNum(initialNum)}>Reset</button>
<button onClick={() => setNum(num + 1)}>Increment</button>
<button onClick={() => setNum(num - 1)}>Decrement</button>
<button onClick={IncrementByFive}>Increment By 5</button>
</div>
);
}
export default App;
IncrementByFive 이 실행되면 그냥 1씩 증가한다. 이는 setState가 비동기함수이기 때문 입니다. 이 때 컴포넌트에서 어떤 상황이 일어나는지 보십시오.
기존 함수는 원래의 진행방향으로 가고 있습니다. 첫 번째로 보이는 페이지에서 num을 변화시켜 리렌더링 시켰으니, 두 번째 페이지가 나오겠죠? 그런데 위에서 적어준 코드는 setState(1 + 1)을 다섯번 해준거나 마찬가지입니다. 이는 첫 번째 보이는 페이지의 num이 1이라고 한다면, 이 때 반복문을 다섯번 돌아주기 때문입니다.
setState(num + 1)이 다섯번 돌아간다는 것은, setState(1 + 1) 이 다섯번 돌아감과 동시에(!!!) 업데이트된 페이지를 렌더링 해줍니다. (비동기함수이기 때문에 반복문과 업데이트가 동시에 진행됩니다!) 따라서 setState(2) 가 다섯번 반복되니 처음 setState말고는 update를 할 필요가 없기 때문에 num이 2인 상태로 렌더링이 되고 마는것이죠.
import React, { useState } from "react";
function App() {
const initialNum = 0;
const [num, setNum] = useState(initialNum);
const IncrementByFive = () => {
for (let i = 0; i < 5; i++) {
setNum((prevNum) => prevNum + 1);
}
};
return (
<div>
<p>Counter: {num}</p>
<button onClick={() => setNum(initialNum)}>Reset</button>
<button onClick={() => setNum(num + 1)}>Increment</button>
<button onClick={() => setNum(num - 1)}>Decrement</button>
<button onClick={IncrementByFive}>Increment By 5</button>
</div>
);
}
export default App;
이 것은 setState의 콜백함수 인자에서 전에 사용하던 state을 가져왔기 때문에 가능한 것입니다.
!https://blog.kakaocdn.net/dn/peE1W/btrwhcVJOH4/OTMFJXp9iVg2M7t5i3qL51/img.png
이런 식으로 원래 컴포넌트에서 setState 가 다섯번 돌아간다고 해도, 기특하게 이전의 setState을 한 state의 값을 기억해서 계속계속 받아와줍니다. 그래서 결국 state가 잘 추가가 되는 것이지요.