Custom Hook 알아보기 - Hook 만들기

해준박·2023년 11월 10일
0
post-thumbnail

전 글에 훅(hook)에 대해 알아보았다. 이제 이 훅들을 만들어서 자신만의 훅인 커스텀훅을 만들고 활용하는법을 알아보자

Custom Hooks 사용 이유

우리는 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에 여러가지 조건을 추가 할 수있었다.


설명을 좀 대충 적었는데 훅을 만드는게 젤 어려운거 같다 설명은 다들 쉽게 하는데 이미 짜여진 코드에서 적용하려고 하니 꽤나 시간이 걸렸다.. 추상화를위해 좀더 작업하고 다른 부분도 훅을 만들어서 좀 더 퀄리티있는 코드로 수정해야 할 것 같다.

profile
기록하기

0개의 댓글