SWR
SeungAh Hong
What is SWR?
- A React Hooks library for remote data fetching, created by zeit, the group behind Next.js
- SWR is a strategy that first returns the data from cache (stale), then sends the fetch request (revalidate), and finally arrives at the up-to-date data
SWR Features
- Simplifies the data fetching logic in your project down to a single line of code
- Fast, lightweight, and reusable data fetching
- Built-in cache and request deduplication
- Real-time experience
- Transport and protocol agnostic
- SSR / ISR / SSG support
- TypeScript ready
- React Native
SWR covers every aspect of speed, correctness, and stability so you can build a better experience
- Fast page navigation
- Interval polling
- Data dependency
- Revalidation on focus
- Revalidation on network recovery
- Local mutation (Optimistic UI)
- Smart error retry
- Pagination and scroll position recovery
- React Suspense
Key Features
- Solving props drilling
// 페이지 컴포넌트
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} />;
}- Global options configuration
<SWRConfig
value={
refreshInterval: 3000,
fetcher: (resource, init) => fetch(resource, init).then(res => res.json()),
}
>
<Dashboard />
</SWRConfig>- Revalidation on focus / interval polling / revalidation on network recovery
useSWR('/api/user', fetcher, {
revalidateOnFocus: true, revalidateOnReconnect : true, refreshInterval: 1000
}- Local mutation (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>
);
}- Smart error retry
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)
}
}- Next.js 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>
);
}- Caching
- TypeScript
- React Native
React Query vs. SWR Comparison
-
Comparison site: https://react-query.tanstack.com/comparison
-
Code comparison Sample code: 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>;
Reference pages