간단한 custom hook 만들어보기 1
이 글은, 노마드코더의 실전형 강의를 참고하여 공부한 내용을 바탕으로 정리하였습니다.
useInput (input을 위한 hook)
useInput.ts
import { useState } from "react";
const useInput = (
// 초기 상탯값 및 벨리데이터 함수(원하는 조건을 리턴하는 함수) 인자로 받음
initialValue: string,
validator: (value: string) => boolean
) => {
// 인자로 받은 값을 기준으로 useState 지정
const [value, setValue] = useState<string>(initialValue);
// validator 타입이 함수가 아닐시, early return
if (typeof validator !== "function") {
return;
}
// 사용할 onChange 함수 구현
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { value } = e.target;
let willUpdate = true;
if (typeof validator === "function") {
willUpdate = validator(value);
}
if (willUpdate) {
setValue(value);
}
};
// 상탯값과 onChange 함수를 Object shorthand로 전달
return { value, onChange };
};
export default useInput;
Input.tsx
import React from "react";
import { useInput } from "src/hooks";
const Input = (): JSX.Element => {
// 원하는 조건의 validator 함수 및 초기 상탯값(여기서는 input의 value값이 된다.) 인자로 전달
const maxLength = (value: string) => value.length <= 10;
const name = useInput("Mr.", maxLength);
return (
<>
<h1>Input</h1>
// {value:value, onChange:onChange} 형태이므로 spread operator로도 전달 가능
<input placeholder="Name" {...name}></input>
</>
);
};
export default Input;
결과
useTabs (탭 전환을 위한 hook)
useTabs.ts
import { useState } from "react";
interface ITab {
tab: string;
content: string;
}
// index에 따라 탭을 전환하기 위해 인자로 초기 상탯값인 index와 전체 배열을 받음
const useTabs = (initialTab: number, allTabs: ITab[]) => {
const [currentIndex, setCurrentIndex] = useState<number>(initialTab);
// 인자로 받은 값(초기 상탯값)을 기준으로 useState 지정
// 현재 아이템을 보여주는 currentItem 및 setState 함수 리턴
return { currentItem: allTabs[currentIndex], changeItem: setCurrentIndex };
};
export default useTabs;
Tab.tsx
import React from "react";
import { useTabs } from "src/hooks";
const Tab = () => {
const {
currentItem: { content },
changeItem,
} = useTabs(1, CONTENTS);
return (
<>
<h1>Tab</h1>
{CONTENTS.map((element, index) => {
const { tab } = element;
console.log(index);
return (
<>
<button
key={index}
onClick={() => {
// 보여주고자 하는 컨텐츠의 index값을 setState함으로써 렌더링
changeItem(index);
}}
>
{tab}
</button>
</>
);
})}
<div>{content}</div>
</>
);
};
export default Tab;
const CONTENTS = [
{ tab: "Section1", content: "I'm the content of the Section1" },
{ tab: "Section2", content: "I'm the content of the Section2" },
];
결과
useTitle(title tag 변화를 위한 hook)
useTitle.ts
import { useState, useEffect } from "react";
const useTitle = (initialTitle: string) => {
// title 태그의 text 요소로 쓰기 위해 초기 상탯값 인자로 전달
const [title, setTitle] = useState<string>(initialTitle);
const updateTitle = () => {
const htmlTitle = document.querySelector("title") as HTMLTitleElement;
htmlTitle.innerText = title;
};
// title값이 변함에 따라 updateTitle 함수가 재실행되도록 의존성 배열 넣기
useEffect(updateTitle, [title]);
return setTitle;
};
export default useTitle;
Title.tsx
import React from "react";
import useTitle from "src/hooks/useTitle";
const Title = (): JSX.Element => {
// titleUpdator 함수가 실행이 되기 전까지 'loading' 텍스트를 띄우기 위해 초기 상탯값으로 인자 전달
const titleUpdator = useTitle("Loading");
setTimeout(() => {
// titleUpdator는 setState 함수이므로 5초뒤 기존 상탯값인 "Loading"에서 "로딩이 끝난 뒤"라는 텍스트로 변화시켜줌
titleUpdator("로딩이 끝난 뒤");
}, 3000);
return (
<>
<h1>Title</h1>
</>
);
};
export default Title;
결과
useClick(click event를 위한 hook)
useClick.ts
import { useRef, useEffect } from "react";
// click의 대상을 특정 element로 특정짓고 싶지 않았기 때문에 HTMLElement를 상속하는 제네릭 타입으로 지정
const useClick = <T extends HTMLElement>(
handleClick: () => void
): React.RefObject<T> => {
const element = useRef<T>(null);
useEffect(() => {
// handleClick 타입이 함수가 아닐시, early return
if (typeof handleClick !== "function") {
return;
}
if (element.current) {
element.current.addEventListener("click", handleClick);
}
return () => {
if (element.current) {
element.current.removeEventListener("click", handleClick);
}
};
}, []);
// useRef 값을 return
return element;
};
export default useClick;
Click.tsx
import React from "react";
import { useClick } from "src/hooks";
const Click = (): JSX.Element => {
const handleClick = (): void => {
console.log("클릭 이벤트 테스트 함수입니다.");
};
// 참조될 element에 따라 제네릭 타입을 다르게 줌으로써, 좀더 자유롭게 useClick 훅을 사용 가능
const title = useClick<HTMLDivElement>(handleClick);
return (
<>
<div ref={title}>Click</div>
</>
);
};
export default Click;
결과
'React' 카테고리의 다른 글
React v18 주요 변경점 (0) | 2022.07.14 |
---|---|
TIL | React + twin.macro(with styled-component) 세팅 (0) | 2022.06.10 |
TIL | useRef의 3가지 정의와 리턴값의 두가지 타입 (0) | 2022.01.12 |
TIL | 간단한 custom hook 만들어보기 2 (0) | 2021.12.20 |
TIL | CRA 없이 React 세팅하기 (0) | 2021.11.25 |