React에서 삽질을 하다가 Form을 만들면서 갑자기 든 생각이었다.
왜 input에 onchange를 사용해서 input 값을 가져올까? 그냥 submit에있는 event 안에 있는 target의 값을 가져오면 되지 않나?
그래서 한번 커스텀 훅을 만들어 보았다… 타스를 제대로 알지 못한채로 작성해서 코드가 맛이없을 수도 있습니다….
일단 전체 코드를 보자
export function useForm<T>(initialValues: T): [T, FormEventHandler<HTMLFormElement>] {
const [data, setData] = useState<T>(initialValues);
const onSubmit: React.FormEventHandler<HTMLFormElement> = (e) => {
e.preventDefault();
const inputs = [...e.currentTarget];
const obj: T = inputs.reduce((acc: T, crr: Element) => {
const { name, value } = crr as HTMLInputElement;
return { ...acc, [name]: value };
}, {} as T);
setData(obj);
};
return [data, onSubmit];
}
이게 내 useForm 훅의 전부이다.
내 useForm은 첫번째 인자로 input의 name을 가진 object를 받는다. 그 후 배열을 반환하는데 배열의 첫번째 값은 값에 접근할 수 있는 object(이때 타입은 useForm의 첫번째 인자와 동일하다.) 두번째는 form에 붙여줄 onSubmit 핸들러이다.
예시 코드를 작성해보자면 다음과 같다.
function App() {
const [data, onSubmit] = useForm<Info>({ name: "", age: "", location: "" });
return (
<>
<form
className="form"
onSubmit={onSubmit}
>
<input
name="location"
type="text"
placeholder="location"
/>
<input
name="name"
type="text"
placeholder="name"
/>
<input
name="age"
type="number"
placeholder="age"
/>
<input type="submit" />
</form>
<div>
<p>Name : {data.name}</p>
<p>age : {data.age}</p>
<p>location : {data.location}</p>
</div>
</>
);
}
export default App;
보면 initialValues 안에 들어간 object의 프로퍼티 이름과 input의 name 이름이 동일한 것을 알 수 있다.
이렇게 해서 submit 버튼을 누르면 input의 name과 initialValues의 프로퍼티 값을 확인해서 값을 넣어준다.
그것이 내 훅의 동작 방법이 되는 것이다. 그럼 이제 코드를 하나씩 뜯어보도록 하자
const [data, setData] = useState < T > initialValues;
데이터를 저장할 useState! 이 친구가 있기 때문에 data값이 form을 통해서 바뀌면 우리의 ui도 바뀔 것이다.
다음으로 onSubmit 핸들러를 보자
const onSubmit: React.FormEventHandler<HTMLFormElement> = (e) => {
e.preventDefault();
const inputs = [...e.currentTarget];
const obj: T = inputs.reduce((acc: T, crr: Element) => {
const { name, value } = crr as HTMLInputElement;
return { ...acc, [name]: value };
}, {} as T);
setData(obj);
};
일단 submit이벤트의 기본동작인 웹페이지 새로고침을 막아주자! e.preventDefault();
다음으로 input들의 dom을 배열로 만들어 주었다.
const inputs = [...e.currentTarget];
📌 event.target vs event.currentTarget
처음에 js로 만들었을 때는 event.target으로 작성하고 잘 동작하는 것을 확인 했다. 하지만…. ts로 넘어와서 작업을 하면서 타입을 넣어주니까 안되길래 삽질끝에 찾아보니 currentTarget을 사용해야 코드가 정상적으로 작동했다.
그렇다면 둘의 차이는 무엇인가?
event.target은 이벤트를 트리거한 요소를 나타낸다. event.currentTarget는 이벤트 핸들러가 연결된 요소를 말한다.. 내 submit이벤트는 form에 연결되어 있으니까 currentTarget을 사용하는 건 이해가 되는데… 왜 target이 안되는지는 잘… 이해를 못하겠지만.. 아무튼 지식이 늘었다! (망할 ts….)
const obj: T = inputs.reduce((acc: T, crr: Element) => {
const { name, value } = crr as HTMLInputElement;
return { ...acc, [name]: value };
}, {} as T);
다음으로 코테에서 많이 사용한 reduce함수를 이용하여 새로운 obj를 만들어 주었다.
input을 한개씩 가져와서 input 안에있는 name과 value를 가지고 initialValues와 같은 타입의 객체를 만들어 주었다.
이후 setData() 에 반환값으로 넘겨주어 data를 저장하고 랜더링한다.
이렇게 나만의 form 커스텀 훅을 만들어 보았다..
고집때문에 삽질하다가 ts와 절교할 뻔했지만… 무사히 넘어갔다.
form안에 input을 제어하는 코드를 onchange를 이용해서 만들길래 뭔가나는 굳이? 싶어서 이런식으로 만들어 봤다. 뭔가 onchange를 사용해야하는 이유가 있다면 알려주세여….
엄청 대단한 건 아니지만 뭔가 커스텀 훅을 만들었다고 생각하니까 큰일을 해낸거 같아서 기쁘다!