React에서 UI와 밀접하게 관련있는 데이터는 State에 보관해줘야 한다.
// 로컬변수를 전달하고 업데이트 해도 React에서 인식하지 못한다.
export default function Counter() {
let num = 0;
return (
<div className="counter">
<span className="number">{num}</span>
// console로 출력해보면 num은 1씩 증가하지만 UI에는 변화가 없다.
<button className="button" onClick={() => {num++; console.log(num)}}>Add +</button>
</div>
);
}
// useState는 변경 가능한 value와 value를 업데이트 할 수 있는 function을 리턴한다.
import React, { useState } from "react";
export default function Counter() {
// useState(0)로 초기값을 설정하면 배열을 리턴한다.
// 배열에는 상태를 확인할 수 있는 let count, 상태를 업데이트 해주는 function setCount
// 통상적으로 function에는 set키워드를 붙힌다.
const [count, setCount] = useState(0);
return (
<div className="counter">
<span className="number">{count}</span>
// onClick이벤트가 발생하면 React는 변경된 값의 함수 전체 즉 Counter컴포넌트를 re-render한다.
// virtual DOM을 통해 이전 DOM요소와 비교 후 변경된 부분만 업데이트 해준다.
// re-render되어도 useState에서 이전 값을 기억하고 있기 때문에 0으로 초기화 되지 않는다.
<button className="button" onClick={() => {setCount(count + 1)}}>Add +</button>
</div>
);
}
만약 아래와 같이 setCount(count + 1)를 5번 작성하면 어떻게 동작할까?
import React, { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return (
<div className="counter">
<span className="number">{count}</span>
<button className="button" onClick={() => {
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
}}>Add +</button>
</div>
);
}
결과는 5씩 증가가 아닌 1씩 증가한다.
이는 Javascript 클로저와 밀접한 관련이 있다.
onClick 이벤트 발생으로 전달되는 콜백함수는 전달 될 때의 상태를 기억한다.
즉 count의 초기값 0을 기억하게 되고 setCount(count + 1)를 5번 실행해도 5번 모두 0 + 1이 되는 것이다.
즉 마지막 실행한 setCount(count + 1)이 최종 값이 된다.
만약 5번 실행한 setCount(count + 1)값이 누적 되길 바란다면
콜백함수를 통해 이전 상태값을 파라미터로 전달하여 누적된 결과값을 출력할 수 있다.
아래와 같이 setCount((prev) => prev + 1)로 작성해야 event 발생 시 5씩 증가하게 된다.
import React, { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return (
<div className="counter">
<span className="number">{count}</span>
<button className="button" onClick={() => {
// 이전 상태값 prev 0
setCount((prev) => prev + 1);
// 이전 상태값 prev 1
setCount((prev) => prev + 1);
// 이전 상태값 prev 2
setCount((prev) => prev + 1);
// 이전 상태값 prev 3
setCount((prev) => prev + 1);
// 이전 상태값 prev 4
setCount((prev) => prev + 1);
// 최종 상태값 5
}}>Add +</button>
</div>
);
}