swr 이슈

마데슾 : My Dev Space·2020년 12월 25일
1

swr

목록 보기
2/2

첫번째 이슈

이슈 내용

FollowList를 swr로 불러오고 있습니다.

user/:userId 이런식으로 routing이 될때만 userId:userId가 할당되고 나머지 경우는 0이 할당됩니다.

export const useFollowingList = ({ userId, me }, offset = 0, limit = 5) => {
  const { data, error, mutate, isValidating } = useSWR(
    `/user/${userId || 0}/followings?offset=${offset}&limit=${limit}`,
  );

  return {
    data: data?.followings,
    isLoading: !error && data === undefined,
    isError: error,
    mutate,
    isValidating,
  };
};

FollowList를 불러오는 코드를 작성하였으니 테스트를 해보겠습니다.

1이라는 유저로 로그인했다가 2라는 유저로 다시 로그인하면 FollowList에 당연히 2유저가 가지고 있는 follow list가 화면에 그려지겠죠?..!

예상대로라면 아래와같이 4명의 팔로워를 가져와야하는데..

1번 유저의 팔로워를 가져오더라구요... ㅜㅜㅜ, 새로고침을 해야 제대로 가져옵니다..

네트워크 탭을 확인해보니.. 새로고침을 하면 아래와같이 get followings 요청이 서버로 잘 가는데, 로그아웃하고 다른 유저로 로그인을 하면 get followings 요청이 가지않습니다..

이유가 뭘까 고민하고, 구글링을 해봐도 나오지 않길래 swr 공식문서를 차근차근 살펴보았습니다.

swr은 데이터 가져 오기를위한 React Hooks 라이브러리라고 합니다.
캐시에서 먼저 데이터를 반환(stale)한 다음 가져 오기 요청(revalidate)을 보내고 마지막으로 최신 데이터를 가져 오는 전략이라고 합니다.

import useSWR from 'swr'
function Profile() {
  const { data, error } = useSWR('/api/user', fetcher)
  if (error) return <div>failed to load</div>
  if (!data) return <div>loading...</div>
  return <div>hello {data.name}!</div>
}

이 예에서 useSWR hook는 key문자열fetcher함수를받습니다. key데이터고유 식별자 (일반적으로 API URL)이며에 fetcher에 전달됩니다. 모든 fetcher는 데이터를 반환하는 비동기 함수일 수 있으며 native fetch 또는 Axios와 같은 도구를 사용할 수 있습니다.

위의 예에서 hook는 요청 상태에 따라 dataerror라는 두 가지 값을 반환합니다 .

보통 우리는 자식 컴포넌트에서 data사용할때 부모컴포넌트에서 data를 가져와 자식 컴포넌트에 props로 넘겨주는데 data를 받을 자식컴포넌트와 부모컴포넌트 사이에 컴포넌트가 더 존재한다면 data를 받을 자식컴포넌트에 도달하기까지 여러개의 컴포넌트에 계속해서 props를 내려줘야하는데 swr을 사용하면 이러한 문제점을 해결할 수 있습니다.

아래는 공식문서 예제입니다.

// page component
function Page () {
  return <div>
    <Navbar />
    <Content />
  </div>
}
// child components
function Navbar () {
  return <div>
    ...
    <Avatar />
  </div>
}
function Content () {
  const { user, isLoading } = useUser()
  if (isLoading) return <Spinner />
  return <h1>Welcome back, {user.name}</h1>
}
function Avatar () {
  const { user, isLoading } = useUser()
  if (isLoading) return <Spinner />
  return <img src={user.avatar} alt={user.name} />
}

이렇게하면 부모컴포넌트로부터 data를 내려받지 않아도되서 독립적인 컴포넌트가 됩니다.
코드가 훨씬 간단해졌네요!

위의 코드에서는 같은 페이지 안에서 const { user, isLoading } = useUser() 동일한 코드를 두번사용했습니다. 코드를 보고서 저는 서버로 요청이 두번 갈거라 예상했습니다. 그런데 swr은 같은 key를 사용해 요청을하면 중복된 요청을 자동으로 제거해준다고 합니다. 한번 요청이 갔을때 캐시에 저장을 하기때문에 같은 함수를 실행해도 key가 같다면 캐시 저장된 값을 꺼내와 사용합니다. 그래서 api 요청이 하나만 전송된 것입니다.

useSWR 함수는 아래와같이 생겼습니다.

const { data, error, isValidating, mutate } = useSWR(key, fetcher, options)

매개변수

  • key: 요청에 대한 고유 키 문자열 (또는 함수 / 배열 / 널) (고급 사용)
  • fetcher: ( 선택 사항 ) 데이터를 가져 오는 Promise 반환 함수 (세부 정보)
  • options : ( 선택 사항 )이 SWR 후크에 대한 옵션 객체

key값 고급사용

조건부 가져 오기

  1. 조건부

    • 조건부로 데이터를 가져 오기 위해 key값에 함수를 사용 하거나 null을 전달합니다. 함수가 잘못된 값을 던지거나 반환하면 SWR은 요청을 시작하지 않습니다.
      // conditionally fetch
      const { data } = useSWR(shouldFetch ? '/api/data' : null, fetcher)
      // ...or return a falsy value
      const { data } = useSWR(() => shouldFetch ? '/api/data' : null, fetcher)
      // ... or throw an error when user.id is not defined
      const { data } = useSWR(() => '/api/data?uid=' + user.id, fetcher)
  2. 종속

    • SWR을 사용하면 다른 데이터에 의존하는 데이터를 가져올 수도 있습니다.
      function MyProjects () {
        const { data: user } = useSWR('/api/user')
        const { data: projects } = useSWR(() => '/api/projects?uid=' + user.id)
        // 함수를 전달할 때 SWR은 반환 값을 '키'로 사용합니다
        // 함수가 거짓을 반환하면 SWR은 일부 종속성이 준비되지 않았음을 알게됩니다.
        if (!projects) return 'loading...'
        return 'You have ' + projects.length + ' projects'
    }

반환값

  • data: 주어진 키에 대한 데이터가 해결됨 fetcher(또는로드되지 않은 경우 정의되지 않음)
  • error: 오류 발생 fetcher(또는 정의되지 않음)
  • isValidating: 요청 또는 재 검증 로딩이있는 경우
  • mutate(data?, shouldRevalidate?): 캐시 된 데이터를 변경하는 기능

swr 중복제거

function useUser () {
  return useSWR('/api/user', fetcher)
}
function Avatar () {
  const { data, error } = useUser()
  if (error) return <Error />
  if (!data) return <Spinner />
  return <img src={data.avatar_url} />
}
function App () {
  return <>
    <Avatar />
    <Avatar />
    <Avatar />
    <Avatar />
    <Avatar />
  </>
}

<Avatar>구성 요소에는 useSWR내부 에 후크가 있습니다. 동일한 SWR 키를 갖고 거의 동시에 렌더링 되므로 네트워크 요청은 1 개만 수행됩니다.

useUser성능이나 중복 요청에 대해 걱정하지 않고 어디에서나 데이터 후크 (위의 예와 같이)를 재사용 할 수 있습니다.

기본 중복 제거 간격을 재정의 하는 dedupingInterval 옵션 도 있습니다.

여기까지 공식문서의 모든 내용은 아니지만 해결방법을 찾기위해 공식문서 일부분을 읽어보았습니다.

다행히도 해결방법이 나와있더라구요 ㅎㅎㅎ

해결방법

👉🏻 문제가 발생한 코드 )

export const useFollowingList = ({ userId, me }, offset = 0, limit = 5) => {
  const { data, error, mutate, isValidating } = useSWR(
    `/user/${userId || 0}/followings?offset=${offset}&limit=${limit}`,
  );

  return {
    data: data?.followings,
    isLoading: !error && data === undefined,
    isError: error,
    mutate,
    isValidating,
  };
};

👉🏻 문제를 해결한 코드 )

export const useFollowingList = ({ userId, me }, offset = 0, limit = 5) => {
  const { data, error, mutate, isValidating } = useSWR(
    // userId가 달라져도 key값으로 들어가는 url에는 항상 0이 들어가기때문에 key가 같아서 재요청을 하지 않는다.
    // me가 없으면 로그아웃 했을 경우이므로 로그아웃일때는 key 값으로 null을 전달하고 로그인했을 경우에는 key 값으로 url을 전달해서 상황에 맞게 key값이 같지 않도록 해주었습니다!
    me ? `/user/${userId || 0}/followings?offset=${offset}&limit=${limit}` : null,
  );

  return {
    data: data?.followings,
    isLoading: !error && data === undefined,
    isError: error,
    mutate,
    isValidating,
  };
};
const {
    data: myFollowingList,
    mutate: myFollowingListMutate,
    isValidating: followingIsValidating,
  } = useFollowingList({ userId, me });
profile
👩🏻‍💻 🚀

0개의 댓글