React Custom Hook - 연습에서 배포까지

중고신입개발자·2022년 1월 23일
0
post-thumbnail

주저리


프로젝트를 하면서 무심코 썼던 커스텀 훅을 제대로 공부하고 싶어졌다.이참에 npm에 첫 배포까지 해보자는 생각으로 nomadcode에서 커스텀 훅 영상을 보고 모르는 것을 정리했다. 강의는 js로 본인은 ts로 진행하였으면 코린이 답게 굉장히 비효율적인 코드가 있을 수 있다.

본론


커스텀훅은 왜 쓸까?

  • react hook의 등장으로 functional components에서 state를 가질 수 있게 되었음. 다시말해 함수형 프로그래밍이 가능해짐.

  • react에서 제공하는 기본 훅을 바탕으로 커스터마이징해서 새로운 기능을 만들어 내었음.

  • 리액트컴포넌트 외부의 다른 함수에서 react components의 이벤트를 처리할 수 있음을 이용해서 파일을 분리하고 개발하면서 커스텀 훅의 장점이 극대화 되었음.

간단한 훅 만들어 보기

기본 훅 없는 패턴

useConfirm

유저가 클릭하면 confirm 창으로 확인해서 기능을 처리하는 커스텀 훅을 만들었다. confirm의 메세지는 string으로 받고, 확인 버튼시 onSuccess를 취소시 onCancle을 실행하는 콜백함수를 받았다. 요즘은 쓰지않는 기본 confirm이지만, confirmAction에 user가 커스텀한 confirm 창을 켜는 기능이 들어간다면 로직은 비슷할 것 같다.

export const useConfirm = (
  message: string = "",
  onSuccess: () => void,
  onCancle?: () => void
) => {
  if (onSuccess && typeof onSuccess !== "function") return;

  const confirmAction = () => {
    if (confirm(message)) {
      onSuccess();
    } else {
      if (!onCancle || typeof onCancle !== "function") return;
      onCancle();
    }
  };

  return confirmAction;
};

useState 활용

useTab : tab contents 만들기

ui 설계시 상당히 많이 쓰이는 tab 컨텐츠다. Tab data를 가지고 있는 array를 이용해서 tab버튼을 만들고, 커스텀 훅을 사용해 currentIndex, currentContents를 리턴하는 커스텀훅을 작성했다. title은 string 으로 제한하고 컨텐츠는 다른 jsx components들을 사용할 수 있게 했다.

type Tab = { tab: string; content: string | JSX.Element | (() => JSX.Element) };

export const useTab = (initialTab: number, allTabs: Tab[]) => {
  const [index, setIndex] = useState<number>(initialTab);

  if (!allTabs || !Array.isArray(allTabs)) return;
  return {
    currentIndex: index,
    changeTabIndex: setIndex,
    currentTab: allTabs[index],
  };
};

useEffect 활용

useScroll : scroll 좌표값 받아오기

윈도우에 이벤트를 달아서 좌표를 state 에 저장 => 리턴해주는 간단한 훅. 확인해야 할 것은 cleanup function에 이벤트를 지워주는 것. components will unmount 일때 해줬던 이벤트 삭제하는 코드가 effect에서는 return 값으로 구현 되는 것을 다시 확인했다.

type Coordinate = {
  x: number;
  y: number;
};

export const useScroll = () => {
  const [state, setState] = useState<Coordinate>({ x: 0, y: 0 });

  const onScroll = () => {
    setState({ x: window.scrollX, y: window.scrollY });
  };

  useEffect(() => {
    window.addEventListener("scroll", onScroll);
    return () => window.removeEventListener("scroll", onScroll);
  }, []);

  return {
    state,
  };
};

useRef 활용

useFadeIn - element fadein

포인트는 ts에서 제네릭으로 HTMLelement를 상속하는 HTML 타입을 받아오게 하는 것, 그리고 Array and object destructing(비구조화)를 활용해서 코드를 깔끔하게 정리하는 것.
발전방향은 intersection observer를 이용해서 web view에 들어왔을 때, 애니메이션을 작동하고 원래상태로 돌리는 것등을 생각해볼 수 있음.

// useFadeIn.ts
const useFadeIn = <T extends HTMLElement>(
  duration: number = 3000,
  delay: number = 0
) => {
  const fadeInElement = useRef<T>(null);

  useEffect(() => {
    if (!fadeInElement.current) return;
    const { current } = fadeInElement;
    current.style.transition = `opacity ${duration}ms ease-in-out ${delay}ms`;
    current.style.opacity = "1";
  }, []);

  if (typeof duration !== "number" || typeof delay !== "number") return;
  return {
    ref: fadeInElement,
    style: { opacity: "0" },
  };
};
// 사용하는 컴포넌트
Header = () => {
  const fadeInHeader = useFadeIn<HTMLHeadingElement>()
	return (<>
    	<h1 {...fadeInHeader}>페이드 인하는 제목입니다</h1>
    </>)
}

그 외 API 정리

const listener: (e: BeforeUnloadEvent) => any = (e) => {
    e.preventDefault();
    e.returnValue = "";
  };
const enablePrevent = () => window.addEventListener("beforeunload", listener);
window.addEventListener('online',callback);
window.addEventListener('offline',callback);
  • Notification: 브라우저에 알림 보내기를 허가 , 알림보내기, 알림설정 띄우기
  const fireNotification = () => {
    // 현재설정 확인
    if (Notification.permission !== "granted") {
      // 알림을 보낼 수 없는 상태면 permission 창 띄우기
      Notification.requestPermission().then((permission) => {
        if (permission === "granted") {
          // 알림 보내기
          new Notification(title, options);
        } else {
          return;
        }
      });
    } else {
      // 알림 보내기
      new Notification(title, options);
    }
  };

NPM 배포

  1. npm organization 만들기

  2. npm init 으로 package.json 생성후 peerDependency 만들기(내가 만든 패키지는 react, react-dom이 필요하다는 것을 알려줌)

    Dependency : 사용하는데 필요한 의존성 모듈객체
    DevDependency : 테스트 관련 모듈이나 트랜스 파일러 관련 모듈처럼 개발 단계에서만 필요한 모듈객체
    peerDependency : 생성한 패키지가 호스트 패키지와 호환성을 가지고 있다는 것을 표현하는 모듈 객체

  1. npm login 로그인 후, npm publish --access public 배포

결론


별거 아닌데 뿌듯하고 좋다. 커스텀훅으로 코드를 줄여보자.
하지만, 무조건 react hook으로 로직을 줄이는 것이 좋은가? 라는 생각이 들때 봐야할 동영상도 첨부한다. 굉장히 많은 도움이 되었고 실제로 실무에서 적용해봐야할 케이스도 많겠지만 클린코드에 대해서 많을 생각을 하게 된 계기가 되었다.
Toss 개발자 진유림님이 설명하는 Frontend 클린코드

profile
취업전에는 기술스택을, 취업후에는 고도화를 하자

0개의 댓글