우리는 보통 useState를 사용할 때 기본적으로
const [state, setState] = useState(0);
의 방식으로 사용한다. useState()
의 인자로 초기값을 넣어주어 처음 상태의 값을 정할 수 있다. 또한 setter함수를 사용하여
setState(1);
상태를 변경을 시킬 수 도 있다. 하지만 항상 이렇게만 사용할 수 있는 것은 아니다.
state가 42라고 가정하고 handle에서 setState를 세 번 호출 해보자
function handleClick() {
setState(state + 1); // setAge(42 + 1)
setState(state + 1); // setAge(42 + 1)
setState(state + 1); // setAge(42 + 1)
}
이 핸들러를 실행하면 state는 45가 되어있을까? 아니다. state는 43이다. 그 이유는 set함수를 호출해도 이미 실행중인 코드에서 state가 업데이트가 되지 않았기 때문에 setState(state+1)
은 setState(43)
이 된다.
이를 해결하기 위해서는 업데이터 함수를 전달할 수 있다.
function handleClick() {
setState(a => a + 1); // setState(42 => 43)
setState(a => a + 1); // setState(43 => 44)
setState(a => a + 1); // setState(44 => 45)
}
setState안에 함수를 업데이터 함수라고 한다. 이 함수는 대기중인 state를 가져와서 다음 state를 계산하게 된다. React에서는 업데이터 함수를 큐에 집어 넣는데 넣고 나서 순서대로 호출하게 된다.
이런 방식을 통해 이전 state를 업데이트 하고 싶다면 업데이터 함수를 사용하여 setter함수를 사용하면 된다.
state는 값이 변할 때 마다 리렌더링이 되는데
import "./styles.css";
import { useState } from "react";
// 무거운 작업이라고 예시 들기
// 함수로 초기값 설정
const heavyWork = () => {
console.log("렌더링 무거운 작업");
return ["김누구", "이누구"];
};
export default function App() {
const [names, setNames] = useState(heavyWork());
//🚨 무거운 작업을 하는 함수를 초기값으로 바로 넣어버리기
const [input, setInput] = useState("");
const inputHandler = (e) => {
setInput(e.target.value);
};
const buttonInputHandler = () => {
setNames((prev) => {
console.log("prev => ", prev);
return [...prev, input];
});
};
return (
<div>
<input type="text" value={input} onChange={inputHandler} />
<button onClick={buttonInputHandler}>버튼</button>
{names.map((name, idx) => {
return <p key={idx}>{name}</p>;
})}
</div>
);
}
요렇게 name state에 계속 추가하면서 값이 변경되면 리렌더링이 반복적으로 일어날 것이다.
이를 해결하기 위해서 어떻게 해야하느냐하면 useState()
의 초기값을 넣어줄 때 함수를 바로 넣어 주는 것이 아니라 콜백함수로 넣어주게 되면
위 사진처럼 첫 렌더링에만 실행이 되고 리렌더시에 실행이 되지 않는 것을 확인할 수 있다. 이렇게 첫 상태로 무거운 작업을 넣게 된다면 바로 넣지않고 콜백형태로 집어넣어 첫 렌더링시에만 무거운 작업을 하도록 하게 하면 된다.
매번 사용하는 useState지만 함수형 업데이트는 생소하다. 이번 기회에 알게 되었으니 무거운 작업이라던지 이전 상태를 바로바로 사용하게 된다면 콜백과 업데이터 함수를 잘 적용해서 사용해 보도록 노력해야겠다.