전 글에 훅(hook)에 대해 알아보았다. 이제 이 훅들을 만들어서 자신만의 훅인 커스텀훅을 만들고 활용하는법을 알아보자
우리는 react로 개발을 할 때, 한 컴포넌트에 여러 state가 생긴다. 또 다른 컴포넌트에도 똑같은 state가 나올 수 있다.
이렇게 반복되는 로직과 state사용을 하나로 묶어 여러 독립적인 컴포넌트에서도 재사용 할 수 있게 만들었다.
개발을 하다 보면 가끔 상태 관련 로직을 컴포넌트 간에 재사용하고 싶은 경우가 생깁니다. 이 문제를 해결하기 위한 전통적인 방법이 두 가지 있었는데, higher-order components와 render props가 바로 그것입니다. Custom Hook은 이들 둘과는 달리 컴포넌트 트리에 새 컴포넌트를 추가하지 않고도 이것을 가능하게 해줍니다. - 리액트 공식 문서-
이렇듯 우리는 한 컴포넌트에서 부모나 자식 컴포넌트에 prop을 넘기는거랑 다르게 여러 컴포넌트에 재사용할 수 있게 로직을 만들어 주면 코드의 유지보수와 길이가 줄어드는 아주 좋은 장점을 가질 수 있음
내 프로젝트에서 hook을 한번 만들어 보려고 끙끙앓았다..
괜히 사용하고싶어서 일부러 코드를 보면서 만들 수 있는게 없나 하다가 일반적으로 많이 사용하는 useInput훅을 커스텀해서 만들었음
지금 프로젝트에서 react-hook-form이라는 편리한 모듈을 사용하는데 내가 커스텀훅을 새로 만들어서 굳이..이기도 싶었다.
// form.tsx
const { register, handleSubmit, setValue, getValues } = useForm<IForm>();
const [nameLength, setNameLength] = useState(0);
const [locationLength, setLocationLength] = useState(0);
const [descriptionLength, setDescriptionLength] = useState(0);
.
.
.
// 가격(price) input 콤마 및 최대 길이
const onChangePriceInput = (e: React.ChangeEvent<HTMLInputElement>) => {
const inputValue = e.target.value;
const maxLength = 11; // 원하는 최대 길이로 설정
// 길이가 최대 길이를 초과하는 경우, 입력값을 최대 길이로 자름
if (inputValue.length > maxLength) {
const trimmedValue = inputValue.slice(0, maxLength);
setValue("price", trimmedValue); // 최대 길이로 자른 값을 필드에 설정
} else {
const numericValue = inputValue.replace(/\D/g, ""); // 숫자 외의 문자 제거
const formattedValue = Number(numericValue).toLocaleString("ko-KR");
setValue("price", formattedValue as any); // 숫자만 입력된 값을 다시 필드에 설정
}
};
// 입력한 글 길이 출력 함수
const onChangeNameInput = (e: IOnChangeFunctionProps) => {
const value = e.target.value;
const name = e.target.name;
if (name === "location") {
if (value.length > 10) {
setValue(name, value.slice(0, 10));
return;
}
setLocationLength(value.length);
}
if (name === "name") {
if (value.length > 20) {
setValue(name, value.slice(0, 20));
return;
}
setNameLength(value.length);
}
if (name === "description") {
if (value.length > 300) {
setValue(name, value.slice(0, 300));
return;
}
setDescriptionLength(value.length);
}
};
수정 전 코드에서는 입력한 글 길이 출력함수를 각각의 input의 name을 받아서 사용하여 구별하고 state들을 업데이트 해주었다.
당시 작업을 할 때 기능구현에 급급하여 이렇게 코드를 짰나보다 중복된로직과 가독성이 좋지않다..
useInput이라는 훅을 작성했다.
import { useState } from "react";
export const useInput = (initialValue: any, initialLength: number, isNumeric: boolean) => {
const [value, setValue] = useState(initialValue);
const [length, setLength] = useState(0);
const onInput = (e: any) => {
const currentValue = e.target.value;
// 길이 제한 및 현재 길이 업데이트
if (currentValue.length > initialLength) {
setValue(currentValue.slice(0, initialLength));
return;
} else {
setValue(currentValue);
}
setLength(currentValue.length);
// 가격부분 숫자만 입력
if (isNumeric) {
const formattedValue = Number(currentValue.replace(/\D/g, "")).toLocaleString("ko-KR");
setValue(formattedValue);
}
};
return {
value,
length,
onInput,
};
};
form에서 useInput()으로 매개변수와 함께 호출하면 state들과 함수들이 있는데, onInput 함수를 호출해 로직을 추가한뒤 setValue이용해 input의 value를 업데이트하여 반환값으로 내보내줄수 있다.
// Form
const nameInput = useInput(product?.name || "", 20, false);
.
.
.
<label>제목</label>
<S.UploadInput
{...register("name", {
required: true,
maxLength: 20,
})}
value={nameInput.value} //
onInput={nameInput.onInput}
placeholder="상품명을 입력해주세요. 20자 이내"
/>
useInput()으로 매개변수들을 보내서 호출할수있는 변수를 생성한다.
nameInput.value
nameInput.onInput
으로 커스텀훅을 호출해 반환하는 값과 로직을 통해 input에 여러가지 조건을 추가 할 수있었다.
설명을 좀 대충 적었는데 훅을 만드는게 젤 어려운거 같다 설명은 다들 쉽게 하는데 이미 짜여진 코드에서 적용하려고 하니 꽤나 시간이 걸렸다.. 추상화를위해 좀더 작업하고 다른 부분도 훅을 만들어서 좀 더 퀄리티있는 코드로 수정해야 할 것 같다.