SWR은 타입스크립트를 완벽하게 지원하고 있습니다. 예제를 보도록 하겠습니다
// swr/types.d.ts
export interface SWRResponse<Data = any, Error = any> {
data?: Data;
error?: Error;
mutate: KeyedMutator<Data>;
isValidating: boolean;
}
import useSWR, { type SWRResponse } from "swr";
interface User {
name:string
age:string
}
export const useUser = (): SWRResponse<User> => {
const swr = useSWR<User>(`/api/user`)
return swr
}
❌❌❌❌
// page.tsx
import { type GetServerSideProps } from "next";
import { unstable_serialize } from "swr";
export const Page = () => {
const { data } = useSWR<User>([`/api/user`,Token]);
return (
<>
<div>{data.data.name}</div>
<div>{data.data.age}</div>
</>
);
};
export const getServerSideProps: GetServerSideProps = async () => {
const userData = await fetcher(`/api/user`,Token);
return {
props: {
fallback:{
['/api/user']:userData,
}
},
};
};
useSWR<User>(`/api/user`);
👆 단일 key를 사용했을 경우의 cache 된 data
useSWR<User>([`/api/user`,Token]);
👆Complex Keys로 넣었을 경우 cache 된 data
props: {
fallback:{
['/api/user']:userData,
}
},
/api/user
] key에 userData를 넘겨주고 있는 모습입니다. 이렇게 되면은useSWR
에서의 key 는 [@"/api/user" , "Token"
] 으로 되어있고getServerSideProps
에서는 [/api/user
] 으로 fallback key를 넘겨 주어 key가 일치하기 않기 때문에 pre-render가 되질 않습니다.✅✅✅✅
// page.tsx
import { type GetServerSideProps } from "next";
import { unstable_serialize } from "swr";
export const Page = () => {
const { data } = useSWR<User>([`/api/user`,Token]);
return (
<>
<div>{data.data.name}</div>
<div>{data.data.age}</div>
</>
);
};
export const getServerSideProps: GetServerSideProps = async () => {
const userData = await fetcher(`/api/user`,Token);
return {
props: {
fallback:{
[unstable_serialize(['/api/user',Token])]: userData,
}
},
};
};
useSWR
은 array 나 function 타입을 key로 사용할 수 있습니다. 이 타입의 키를 사용하기 위해선 fallback key들을 unstable_serialize
와 함께 직렬화해주어야 동일한 key 를 가질 수 있습니다// useOnScreen.ts
import { useState, useEffect, type RefObject } from "react";
interface Params {
isOnce?: boolean;
}
/** IO Custom hooks
* @param {HTMLElement} ref - HTML Element
* @param {boolean} prarms.isOnce - true 일 경우 IO 가 헌반만 체크된 후 요소 감시를 멈춤니다.
* */
export default function useOnScreen<T>(ref: RefObject<T>, { isOnce = true }: Params = {}) {
const [isIntersecting, setIntersecting] = useState<boolean>(false);
useEffect(() => {
if (!ref.current) return;
const observer = new IntersectionObserver(([entry], _observer) => {
if (!isOnce) {
setIntersecting(entry.isIntersecting);
return;
}
if (entry.isIntersecting) {
setIntersecting(true);
_observer.disconnect();
}
});
if (ref.current) {
observer.observe(ref.current);
}
return () => {
observer.disconnect();
};
}, [ref.current]);
return isIntersecting;
}
Intersection Observer API를 사용하기 재사용 하기 위해 간단한 hook을 만들겠습니다
// page.tsx
import UserInfo from "~/components/Template/UserInfo";
const MainPage = () => {
return (
<>
<Container>아무 의미 없는 글인데 /api/user Data는 밑에 안 보이는곳에 있어요 👇👇</Container>
<Container2>👇👇👇👇👇👇👇</Container2>
<UserInfo />
</>
);
};
// ~/components/Template/UserInfo
import { useRef } from "react";
import useSWR, { type SWRResponse } from "swr";
import useOnScreen from "~/hooks/useOnScreen";
interface User {
name: string;
age: number;
}
// SWR custom hook
const useUser = (visible: boolean = true): SWRResponse<User> => {
const swr = useSWR<User>(visible ? [`/api/user`, "Token"] : null);
return swr;
};
const UserInfo = () => {
const obRef = useRef<HTMLDivElement>(null);
const visible = useOnScreen<HTMLDivElement>(obRef);
const { data: userData } = useUser(visible);
if (!userData) return <div ref={obRef}>loading</div>;
return (
<div>
<div>{userData?.name}</div>
<div>아직은{userData?.age}살</div>
</div>
);
};
다음 포스팅에서는 useSWRInfinite 과 React 18 의 Suspense 를 다루어 보도록 하겠습니다
3개 이어진 포스트 모두 읽어봤는데 되게 깔끔하게 잘 작성하셨네요.
덕분에 잘 배우고 갑니다:)