SWR 이란?
- NextJS를 만든 zeit이라는 그룹에서 만든 React Hooks for Remote Data Fetching Library
- SWR은 먼저 캐시(스태일)로부터 데이터를 반환한 후, fetch 요청(재검증)을 하고, 최종적으로 최신화된 데이터를 가져오는 전략
SWR 기능
- 단 한 줄의 코드로 프로젝트 내의 데이터 가져오기 로직을 단순화
- 빠르고, 가볍고, 재사용 가능한 데이터 가져오기
- 내장된 캐시 및 요청 중복 제거
- 실시간 경험
- 전송 및 프로토콜에 구애받지 않음
- SSR / ISR / SSG support
- TypeScript 준비
- React Native
SWR은 더 나은 경험을 구축할 수 있도록 속도, 정확성, 안정성의 모든 측면
- 빠른 페이지 네비게이션
- 인터벌 폴링
- 데이터 의존성
- 포커스시 재검증
- 네트워크 회복시 재검증
- 로컬 뮤테이션(Optimistic UI)
- 스마트한 에러 재시도
- 페이지 및 스크롤 위치 복구
- React Suspense
주요기능
- props drills 해결
// 페이지 컴포넌트
function Page() {
const [user, setUser] = useState(null);
// 데이터 가져오기
useEffect(() => {
fetch('/api/user')
.then(res => res.json())
.then(data => setUser(data));
}, []);
// 전역 로딩 상태
if (!user) return <Spinner />;
return (
<div>
<Navbar user={user} />
<Content user={user} />
</div>
);
}
// 자식 컴포넌트
function Navbar({ user }) {
return (
<div>
...
<Avatar user={user} />
</div>
);
}
--swr;
function Content() {
const { user, isLoading } = useSWR('/api/user');
if (isLoading) return <Spinner />;
return <h1>Welcome back, {user.name}</h1>;
}
function Avatar() {
const { user, isLoading } = useSWR('/api/user');
if (isLoading) return <Spinner />;
return <img src={user.avatar} alt={user.name} />;
}
- 글로벌 옵션설정
<SWRConfig
value={
refreshInterval: 3000,
fetcher: (resource, init) => fetch(resource, init).then(res => res.json()),
}
>
<Dashboard />
</SWRConfig>
- 포커스시 재검증/인터벌 폴링/네트워크 회복시 재검증
useSWR('/api/user', fetcher, {
revalidateOnFocus: true, revalidateOnReconnect : true, refreshInterval: 1000
}
- 로컬 뮤테이션(Optimistic UI)
import useSWR, { useSWRConfig } from 'swr';
function App() {
const { mutate } = useSWRConfig();
return (
<div>
<Profile />
<button
onClick={() => {
// set the cookie as expired
document.cookie =
'token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
// tell all SWRs with this key to revalidate
mutate('/api/user');
}}
>
Logout
</button>
</div>
);
}
- 스마트한 에러 재시도
useSWR('/api/user', fetcher, {
onErrorRetry: (error, key, config, revalidate, { retryCount }) => {
// Never retry on 404.
if (error.status === 404) return
// Never retry for a specific key.
if (key === '/api/user') return
// Only retry up to 10 times.
if (retryCount >= 10) return
// Retry after 5 seconds.
setTimeout(() => revalidate({ retryCount }), 5000)
}
}
- NextJS SSG and SSR
export async function getStaticProps() {
// `getStaticProps` is executed on the server side.
const article = await getArticleFromAPI();
return {
props: {
fallback: {
'/api/article': article,
},
},
};
}
function Article() {
// `data` will always be available as it's in `fallback`.
const { data } = useSWR('/api/article', fetcher);
return <h1>{data.title}</h1>;
}
export default function Page({ fallback }) {
// SWR hooks inside the `SWRConfig` boundary will use those values.
return (
<SWRConfig value={fallback}>
<Article />
</SWRConfig>
);
}
- React Suspense/ErrorBoundary
import { Suspense } from 'react';
import useSWR from 'swr';
function Profile() {
const { data } = useSWR('/api/user', fetcher, { suspense: true });
return <div>hello, {data.name}</div>;
}
function App() {
return (
<ErrorBoundary FallbackComponent={<div>error....</div>}>
<Suspense fallback={<div>loading...</div>}>
<Profile />
</Suspense>
</ErrorBoundary>
);
}
- 캐싱
- TypeScript
- React Native
React Query, SWR 비교
-
코드비교 샘플코드: https://github.com/seungahhong/states-todos
-- services export const fetcher = (...args) => axios.get(...args); export const fetchTodos = (id) => `https://jsonplaceholder.typicode.com/todos${id ? `/${id}` : ''}`; -- react query export const useFetchTodo = ({ id, suspense } ) => { const { data, error } = useQuery(fetchTodos(id), () => fetcher(fetchTodos(id)), { suspense: !!suspense, useErrorBoundary: true }); return { data, isLoading: !data && !error, error, } } -- swr useSWR(fetchTodos(id), () => fetcher(fetchTodos(id))) useSWR(fetchTodos(id)', url => fetcher(url)) useSWR(fetchTodos(id), fetcher) export const useFetchTodo = ({ id, suspense } ) => { const { data, error } = useSWR(fetchTodos(id), fetcher, { suspense: !!suspense }); return { data, isLoading: !data && !error, error, } } const { data: response } = useFetchTodo({ id: 1, suspense: true }); // const { data: response } = useFetchTodo({ id: 1 }); // if (isLoading) return <div>loading...</div>; // if (error) return <div>error....</div>;
참고페이지