useState는 React에서 가장 많이 쓰이는 Hook이죠.
평소처럼 useState를 잘 쓰다가 우테코 장바구니 미션에서 문제에 직면하게 되었어요.
⚠️ 문제 상황
CheckBox Component 내부의 useState의 초기값을 props로 받은 인자로 초기화를 했더니 체크박스 하나를 클릭해도 존재하는 모든 체크박스가 체크되고 해제되는 에러가 발생했습니다.
export default function CheckBox({
initialChecked = false,
}) {
const [isChecked, setIsChecked] = useState(initialChecked); // 문제 발생!
...
}
👉 체크박스를 클릭하면 App의 상태가 변하지만 Child 컴포넌트는 변하지 않았어요. 왜 이럴까요?
먼저 useState의 동작 방식에 대해서 살펴봐야겠죠?
const [state, setState] = useState(initiailState)
하지만
setState가 state를 변경시키다고 오해
하는 경우가 많아요.
useState같은 Hook들은 react 모듈 안에 선언된 함수예요.
useState는 실행될 때마다 dispatcher를 선언하고 useState 메서드를 실행해서 그 값을 반환해요.
선언한 dispatcher코드를 살펴보면 전역 변수 ReactCurrentDispatcher로부터 dispatcher를 가져옴을 확인할 수 있어요.
👉 함수가 선언부(dispatcher)보다 상위에 있는 값(ReactCurrentDispatcher)에 접근하고 있죠? 즉, Closure의 개념이 등장하게 됩니다.
👆 useState는 _value 라는 전역에 선언된 변수를 참조하고 있어요. 이것이 바로 우리가 관리하는 ‘상태’이죠!
자신이 선언된 위치에서 접근할 수 있는 _value 상태를 변경하는 것입니다.
if(_value === undefined)
_value = initialValue;
👆 보시다시피 최초 호출에만 초기값을 할당하고 이후에는 initialValue가 아예 사용되지 않죠?
자 그럼 길게 흐름을 봅시다.
처음 컴포넌트 함수가 실행되어 useState를 실행합니다. 👉 _value가 undefined이니 initialValue를 할당합니다.
다시 컴포넌트 함수가 실행되면 useState가 실행됩니다. 👉 이 때에는_value가 undefined가 아니죠. 👉 useState가 반환한 값을 변수에 할당합니다.
setState함수는 자신과 함께 반환된 변수를 변경시키는 것이 아니라 다음 useState가 반환할 react 모듈의 _value를 변경
시키고 컴포넌트를 리렌더링 시키는(트리거) 역할을 합니다.
변경된 값은 다음 컴포넌트 함수가 실행될 때 useState가 가져오기 때문에
setState 호출 이후 로직에서도 state의 값은 이전과 동일합니다.
Q: 최신의 state는 누가 가져오나요?
A: useState가 state를 가져옵니다.
Q: state는 누가 변경시키나요?
A: state를 변경한다기보다는 _value값을 변경하는거죠. setState가 _value를 변경하고 이를 배열 구조 분해 할당으로 가져와서 사용하는 것입니다.
useState는 딱 한 번만 실행되기 때문이죠.
❗해결방법은 다음과 같습니다.
export default function CheckBox({
initialChecked = false,
}) {
const [isChecked, setIsChecked] = useState(false); // 초기값을 props로 설정하면 안됩니다.
useEffect(() => {
setIsChecked(initialChecked);
}, [initialChecked]); // 이렇게 useEffect를 통해 props가 바뀔때마다 setState를 해줍니다.
👆 이렇게 initialChecked를 useState의 인자로 넘기지 않아야 변화하는 initialChecked라는 값을 지속적으로 추적할 수 있겠죠.
Do not use props as default value of React.useState() directly | by Allen Wei | Medium
안녕하세요. 좋은 글 감사합니다 :)
useState의 lazy initialization 기능에 대해 공부하면서 useState 코드를 까보다 보니 이해가 안가는 부분이 많아서 글을 타고타고 넘어와 Nine님의 글을 읽게 되었습니다 🤓
실례가 되지 않는다면 'useState를 까보면...' 에서 보여주신 코드의 출처가 어떻게 되는지 알려주실 수 있을까요??
facebook/react github에서 열심히 찾아보면서 공부중인데 해당 코드를 못찾겠어서,, 도움을 받을 수 있다면 답변 한번 부탁드립니다!