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
는 요청 상태에 따라 data
및 error
라는 두 가지 값을 반환합니다 .
보통 우리는 자식 컴포넌트에서 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
값에 함수를 사용 하거나 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)
종속
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?)
: 캐시 된 데이터를 변경하는 기능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 });