이름 그대로 개발자가 스스로 커스텀 한 훅(함수)을 의미한다.
custom hook을 사용하게되면 함수 네이밍에 use를 사용해줘야 한다.
use를 붙이지 않았을 경우 실행은 되지만 의도와는 다르게 작동되거나 훅의 에러 로그를 볼 수 없게 되고 에러 핸들링이 굉장히 어려워질 수 있다.
https://ko.reactjs.org/docs/hooks-custom.html
- src/components/commons/hooks/useAuth.tsx
import { useRouter } from 'next/router' import { useEffect } from 'react' function useAuth(){ const router = useRouter() // useEffect 훅스를 사용하고 있기 때문에 커스텀 훅스입니다. useEffect(()=>{ if(!localStorage.getItem("accessToken"){ alert("로그인 후 이용 가능합니다!") void router.push("/23-03-login-check") } },[]) }
custom hook은 컴포넌트가 아닌 함수이기 때문에 return 부분이 JSX가 아님!
- custom-hooks-use-auth 폴더의 index.tsx
// useAuth를 실행할 페이지 import { useAuth } from "../../../src/components/commons/hooks/useAuth"; export default function CustomHooksUseAuthPage(): JSX.Element { // 1. 왜 hoc처럼 어렵게 했나? class컴포넌트는 hooks이 안되므로 // 2. custom-hooks라고 했는데, 결국 그냥 함수 아닌겨? 함수 맞음~~이름변경도 가능해! // 3. custom-hooks와 함수의 차이는 뭔가? // 둘다 함수지만 custom-hooks를 구별하는 이유는 해당 함수 안에서 다른 함수를 사용하고 있기 때문임. // 구별 기준 : 내부에 다른 hooks를 포함하고 있는가 useAuth(); return <div>PROFILE PAGE</div>; }
해당 페이지가 실행되면, 상단의 useAuth() 먼저 실행된다.
그럼 토큰을 확인 후 토큰이 존재한다면 프로필 페이지가 정상적으로 작동하고, 그렇지 않다면 로그인 후에 이용하도록 경고창을 띄워주도록 유도할 것이다.
매번 router를 불러오고 코드를 직접 작성해서 페이지 이동하지 않고, custom hooks로 분리해 관리한다.
- custom-hooks-use-move-to-page 폴더의 index.tsx
// import { useRouter } from "next/router"; import { useMoveToPage } from "../../../src/components/commons/hooks/useMoveToPage"; export default function CustomHooksUseAuthPage(): JSX.Element { // const router = useRouter(); // const onClickMoveToBoard = () => {}; // const onClickMoveToMarket = () => {}; // const onClickMoveToMyPage = () => {}; // const onClickMoveToPage = (path: string) => () => { // void router.push(path); // }; // const result = useMoveToPage(); // 커스텀 훅으로 분리한 라우터 사용 -> 리턴을 객체로했기때문에 객체로 받아오는 것!! const { onClickMoveToPage } = useMoveToPage(); return ( <> {/* <button onClick={onClickMoveToBoard}>게시판이동</button> <button onClick={onClickMoveToMarket}>마켓이동</button> <button onClick={onClickMoveToMyPage}>마이페이지이동</button> */} <button onClick={onClickMoveToPage("/boards")}>게시판이동</button> <button onClick={onClickMoveToPage("/products")}>마켓이동</button> <button onClick={onClickMoveToPage("/mypages")}>마이페이지이동</button> </> ); }
- src/components/commons/hooks/useMoveToPage.tsx
import { useRouter } from "next/router"; import { useRecoilState } from "recoil"; import { visitedPageState } from "../../../commons/stores"; interface IUseMoveToPageReturn { visitedPage: string; onClickMoveToPage: (path: string) => () => void; } export const useMoveToPage = (): IUseMoveToPageReturn => { const router = useRouter(); const [visitedPage, setVisitedPage] = useRecoilState(visitedPageState); // path기록시 글로벌스테이트에 저장할 것 const onClickMoveToPage = (path: string) => () => { // localStorage.setItem("visitedPage",path) 로컬스토리지도 가능! setVisitedPage(path); //로그인페이지일 때는 set하지 않도록 조건이 추가되야 함. void router.push(path); }; return { visitedPage, onClickMoveToPage, }; };
함수의 인자로 라우팅할 주소를 넘겨주면 해당 페이지로 라우팅 된다.
다녀간 페이지들은 RecoilState에 넣어두어 필요한 시기에 적절한 시점에 사용해주면 된다.
또한 리턴은 하나만 해줄 수 있으므로 객체로 묶어 리턴을 해준다.