홍승아블로그

SWR

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://react-query.tanstack.com/comparison

  • 코드비교 샘플코드: 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>;

참고페이지

https://swr.vercel.app/ko/docs/getting-started

https://swr.vercel.app/ko

이전글
form
다음글
canvas