함수 컴포넌트에서도 클래스 컴포넌트와 같이 state와 라이프사이클 API를 사용할 수 있도록 도입된 'Hooks'. 프로젝트를 진행하면서 useState, useEffect, useHistory와 같은 내장 Hooks는 사용해 봤지만 내 프로젝트에서 재사용이 가능한 함수를 뽑아내 Custom Hooks로 만들어 내는 공부를 추가적으로 하고 싶어서 공부를 시작했다.
사이트에서 가장 많이 쓰이는 것이 input이라고 생각된다. 로그인, 회원가입, 검색, 구매 등등 그래서 input을 관리하기 위한 Hooks를 만들었다. 인자로 초기 input value와 input의 작성 조건 함수를 받아 사용되는 Hooks이다.
const useInput = (initialValue = "", conditions) => {
const [value, setValue] = useState(initialValue);
// input에 들어오는 값을 관리하기 위해 useState Hooks를 사용한다.
if (typeof conditions !== "function") {
return;
}
// 인자로 들어오는 'conditions'가 함수가 아닐 경우 실행되지 않는다.
const onChange = (event) => {
const { value } = event.target;
if (conditions(value)) {
setValue(value);
}
};
// input에 onChange 이벤트가 있을 때마다 조건을 확인하여 input에 value를 저장한다.
return { value, onChange };
};
// 사용 예시
const App = () => {
const inputCondition = (value) => value.length < 10;
// input value가 10자 미만일 때까지만 작성이 가능하다.
const handleInput = useInput("", inputCondition);
// handleInput은 useInput Hooks로 input을 관리한다.
return (
<div>
<h1>React</h1>
<input type="text" {...handleInput} />
// handleInput을 spread해서 Hooks의 value와 onChange 이벤트를 사용한다.
</div>
);
};
탭을 관리하는 Hooks로 전체 전체 탭 정보(탭명과 내용) 그리고 페이지 최초 로드 시 노출되는 디폴트 탭의 index를 인자로 받는 Hooks이다.
const content = [
{
tab: "Section1",
content: "I'm Section1"
},
{
tab: "Section2",
content: "I'm Section2"
}
];
const useTabs = (initialIndex, allTabs) => {
if (!allTabs || !Array.isArray(allTabs)) {
return;
}
// Hooks 사용이 가능한 탭 정보의 형식을 검사한다.
const [currentIndex, setCurrentIndex] = useState(initialIndex);
// 노출해야 할 탭의 인덱스를 관리한다.
return {
currentItem: allTabs[currentIndex],
changeItem: setCurrentIndex
};
// Hooks로 현재 활성화된 탭의 내용과 인덱스 변경 함수를 리턴한다.
};
// 사용 예시
const App = () => {
const { currentItem, changeItem } = useTabs(0, content);
return (
<div>
{content.map((section, index) => {
return <button onClick={() => changeItem(index)}>{section.tab}</button>;
})}
{currentItem.content}
</div>
);
};
사이트 화면이나 카테고리가 변경될 때 title이 변경되도록하는 Hooks이다.
const useTitle = (initialTitle) => {
const [title, setTitle] = useState(initialTitle);
// initialTitle의 초기값으로 갖고 title의 상태를 관리한다.
const handleTitle = () => {
const isTitle = document.querySelector("title");
isTitle.innerText = title;
};
// title을 변경하는 함수를 선언한다.
useEffect(handleTitle, [title]);
// title이 변경될 때마다 title을 실제로 변경하는 함수를 호출한다.
return setTitle;
};
// 사용 예시
const App = () => {
const titleUpdate = useTitle("Loading...");
// 사이트 최초 로드 시 "Loading..."이라는 타이틀이 노출 되도록 한다.
setTimeout(() => {
titleUpdate("home");
}, 2000);
// 2000밀리초 후 "home"으로 title을 변경한다.
return <div>hi</div>;
};
가장 많이 쓰이는 이벤트 중 하나인 Click 이벤트를 Hooks로 만들어 여러 요소 중 특정 요소에 클릭 이벤트를 부여하고자 할 때 사용 할 수 있도록 했다.
const useClick = (onClickEvent) => {
if (typeof onClickEvent !== "function") {
return;
}
// onClickEvent가 함수인지 판별한다.
const element = useRef();
// 각 요소들을 개별로 관리하기 위해 useRef Hooks를 사용한다.
useEffect(() => {
if (element.current) {
element.current.addEventListener("click", onClickEvent);
}
// ComponentDidMount로 요소에 클릭 이벤트를 부여한다.
return () => {
element.current.removeEventLsitener("click", onClickEvent);
};
// ComponentWillUnMount로 요소에 클릭 이벤트를 제거한다.
}, []);
return element;
};
// 사용 예시
const App = () => {
const sayHi = () => console.log("Hi");
const title = useClick(sayHi);
return <h1 ref={title}>hi</h1>;
};
window 내장 함수인 confirm
을 사용하기 위해 confirm메세지, 확인 클릭 시 이벤트, 취소 클릭 시 이벤트를 인자로 받는 Hooks를 만든다.
const useConfirm = (message = "", onConfirm, reject) => {
if (!onConfirm || typeof onConfirm !== "function") {
return;
}
// confirm 확인 시 호출되는 함수를 검사한다.
if (!reject || typeof reject !== "function") {
return;
}
// confirm 취소 시 호출되는 함수를 검사한다.
const confirmAction = () => {
if (window.confirm(message)) {
onConfirm();
} else {
reject();
}
};
// confirm 확인/취소시 호출 될 함수를 정리한다.
return confirmAction;
};
// 사용 예시
const App = () => {
const sayHi = () => console.log("hi");
const sayNo = () => console.log("I hate you");
const hadleConfirm = useConfirm("Are you sure?", sayHi, sayNo);
return <h1 onClick={hadleConfirm}>hi</h1>;
};
사용자가 페이지를 떠날 때 정말로 떠날 것인지 묻는 대화 상자를 띄우는 Hooks를 만들었다!
const usePrevent = () => {
const onLeave = (event) => {
event.preventDefault();
// 이벤트를 취소할 수 있는 경우, 이벤트의 전파를 막지않고 그 이벤트를 취소한다.
event.returnValue = "";
// Chrome에서 필요한 설정이다!
};
const onPrevent = () => {
window.addEventListener("beforeunload", onLeave);
};
// 사용자가 페이지를 떠날 때 정말로 떠날 것인지 묻는 확인 대화 상자를 표시한다.
const removePrevent = () => {
window.removeEventListener("beforeunload", onLeave);
};
// 사용자가 페이지를 떠날 때 정말로 떠날 것인지 묻는 확인 대화 상자를 표시하는 이벤트를 제거한다.
return { onPrevent, removePrevent };
};
const App = () => {
const { onPrevent, removePrevent } = usePrevent();
return (
<div>
<button onClick={onPrevent}>prevent</button>
<button onClick={removePrevent}>no prevent</button>
</div>
);
};
사용자의 마우스가 창을 떠나려고 할 때 경고의 메세지(?)를 보낼 수 있는 Hooks이다. 실제 커머스 사이트에서 사용자가 창을 닫으려고 할 때 배너를 띄우는 등의 요소를 줄 때가 있는데 이렇게 Hooks로 만들어 놓으면 정말 많이 사용할 수 있을 것 같다.
const useBeforeLeave = (Notice) => {
if (typeof Notice !== "function") {
return;
}
const leaveCondition = (event) => {
const { clientY } = event;
if (clientY <= 0) {
Notice();
}
};
// 마우스가 창의 위로 갈 때만 함수가 실행되도록 한다.
useEffect(() => {
document.addEventListener("mouseleave", leaveCondition);
return () => {
document.removeEventListener("mouseleave", leaveCondition);
};
}, []);
// componentDidMount로 이벤트가 추가되고 componentWillUnmount로 이벤트를 제거한다.
};
// 사용 예시
const App = () => {
const userNotice = () => {alert("정말 떠나실 건가요??")}
useBeforeLeave(userNotice);
// useState, useEffect 등의 사용이 없기 때문에 useBeforeLeave 함수를 바로 호출한다.
return (
<div>
<h1>React</h1>
<input type="text" />
</div>
);
};