아래 코드는 custom hook으로,
form을 다루는 컴포넌트들에서 input을 관리하는 기능을 묶어서 정의한 것이다.
import { useState, useCallback } from "react";
const useInput = (initialInput) => {
const [input, setInput] = useState(initialInput);
const onChangeInput = useCallback((e) => {
// const { name, value } = e.target;
console.log(e);
setInput((input) => {
return {
...input,
[e.target.name]: e.target.value,
};
});
}, []);
return [input, onChangeInput];
};
export default useInput;
그런데 input을 작성하다 보면 아래와 같은 에러가 뜬다.
아래와 같이 바꾸니 문제가 해결되는 것을 알 수 있었다.
그러나 이유를 이해하지 못했기 때문에, 찾아보기로 하였다.
react에서 이벤트 핸들러는 모든 브라우저에서 동일하게 작동하는 것을 보장하기 위해 SyntheticEvent 객체로 브라우저의 event를 감싸서 전달한다고 한다.
그런데 이 객체가 작동하는 방식이 신기하다.
이 객체는 성능상의 이점을 위해 계속해서 재사용되는 방식으로 이루어진다.
이를 event pooling이라고 하더라. (다음 버전의 react에서 사라질 예정이라고 한다.)
간단하게 말하자면 이벤트 핸들러가 쭉 실행되고 나서 SyntheticEvent 객체의 모든 요소를
nullify
하는 식으로 비운다고 한다.object pooling 설명 링크
이 작동 방식 설명을 위해 react 공식 사이트에는 다음과 같은가 있었다.
function handleChange(e) {
// This won't work because the event object gets reused.
setTimeout(() => {
console.log(e.target.value); // Too late!
}, 100);
}
즉 setTimeout 안의 e.target.value는 원하는 대상을 지칭하지 않게 된다.
react에서
object pooling
방식에 의해 계속해서 재사용 된다는 점**즉 이벤트 핸들러가 시행되고 나면 객체 내부의 내용이 초기화 된다는 점.
이 두 가지에 의해 맨 꼭대기에서 확인할 수 있는 error가 뜬 것이다.
따라서 그 내부의 값을 우선 다른 변수에 할당 한 후,
그 할당한 값을 이용해서 state를 변경하면 오류가 없이 잘 작동하는 것을 알 수 있었다.