서버 상태관리를 하는 대표적인 두가지 라이브러리는 RactQuery와 SWR이 있다.
즉, 두 라이브러리 모두 data fetch를 도와주는 라이브러리이다.
사용하는 이유가 무엇일까?
- 어떤 데이터를 언제 fetch하면 되는지 목표만 기술하면 된다.
- 동일한 API 요청이 여러 번 호출될 경우 한 번만 실행
- 자연스럽게 흐름 파악 가능
SWR
import useSWR from "swr";
function Profile() {
const { data, error } = useSWR("/api/user", fetcher, options);
if (error) return <div>failed to load</div>;
if (!data) return <div>loading...</div>;
return <div>hello {data.name}!</div>;
}
data와 error 명시
첫 번째 인자는 key값이며 두 번째 인자는 fetcher함수이다.
fetcher함수의 key값이 첫 번째 파라미터로 전달된다.
promise를 리턴하는 모든 함수가 올 수 있어, 기본 fetch함수 뿐만 아니라, axios 등과 같은 별도 라이브러리가 사용가능하다.
세번째 파라미터는 options로, 사용자가 페이지를 탐색 중 다른 탭을 보다가 다시 돌아왔을 때 네트워크가 끊어졌다가 다시 연결되었을 때 refetch할 수 있도록 옵션을 설정할 수 있다.
React Query
import { useQuery } from "react-query";
const useUser = id => {
const result = useQuery(`/user/${id}`, fetcher, options);
return result;
};
function Example() {
const { isLoading, error, data } = useUser("123");
if (isLoading) return "Loading...";
if (error) return "An error has occurred: " + error.message;
return <div>hello {data.name}!</div>;
}
SWR과 비슷한 사용방법으로,
첫 번째 인자는 unique한 key값으로 url이나 unique한 값 (string, array)이라면 뭐든지 가능하다.
key를 배열로 사용할 때 주의할 점 object가 포함되었을 경우 object는 항목들이 동일하다면(키 값이 같으면?) 명시 순서가 다르더라도 동일한 key를 취급하지만, array를 키로 설정할경우 순서가 다르면 다른 키로 취급된다.
두 번째 인자는 fetcher함수이다. promise를 리턴하는 모든 함수가 올 수 있으며, fetch 함수 뿐만 아니라, axios도 가능하다.
data fetching 라이브러리를 사용하면, 데이터를 쉽게 가져올 수 있고, 동일한 요청을 하는 여러 컴포넌트가 동시에 렌더링 되더라도 한 번만 요청한다.
백그라운드에서 서버에 주기적으로 polling을 하면서 데이터가 유효한지 검사하고, 유효하지 않으면 업데이트하여 데이터를 동기화한다.
state에 따라 api요청을 막고싶은 경우는?
SWR
// state에 따라 key 값을 null로 만들면 fetch가 실행되지 않습니다.
const { data } = useSWR(shouldFetch ? "/api/data" : null, fetcher);
// key 함수가 falsy 값을 리턴하면 fetch가 실행되지 않습니다.
const { data } = useSWR(() => (shouldFetch ? "/api/data" : null), fetcher);
// user가 존재하지 않아서 user.id에 접근할 때 error가 발생해 fetch가 실행되지 않습니다.
const { data } = useSWR(() => "/api/data?uid=" + user.id, fetcher);
key 값이 null 이거나 key 대신 함수를 사용할 경우 falsy한 값을 return 하면 fetch를 멈출 수 있습니다.
React Query
const { isIdle, data } = useQuery(["todos", userId], getTodosByUser, {
// 쿼리는 userId 값이 존재할 때까지 실행되지 않습니다
enabled: !!userId,
});
// isIdle 값은 enabled가 true가 될 때까지, fetch가 시작되기 전까지 true입니다.
사용자가 프로필 수정 기능을 통해 데이터를 수정해서, 프로필 정보를 refetch 하고 싶은 경우에는?
SWR
import { useSWRConfig } from "swr";
const Profile = () => {
const { mutate } = useSWRConfig();
const [profile, setProfile] = useState({ name: "jojiiiiyoung" });
const handleEditProfile = () => {
await updateProfile(profile);
// unique key를 통해서 data가 invalid 상태임을 전달합니다.
mutate(`/user/${userId}`);
};
};
mutate 라는 함수를 통해 키에 해당하는 query를 refetch하도록 요청할 수 있다.
ReactQuery
import { useQueryClient } from "react-query";
const Profile = () => {
const queryClient = useQueryClient();
const [profile, setProfile] = useState({ name: "jojiiiiyoung" });
const handleEditProfile = () => {
await updateProfile(profile);
// unique key를 통해서 data가 invalid 상태임을 전달합니다.
// react query에서는 해당 데이터를 즉시 refetch 하게 됩니다.
queryClient.invalidateQueries(`/user/${userId}`);
};
invalidateQueries 함수를 통해 해당 key의 query가 invalid 하다고 알려주어, refetch하도록 한다.
사용자가 클라이언트에서 프로필을 수정하는 경우, 개발자는 이미 데이터가 어떻게 바뀌었는지 알 수 있는데요.refetch를 하지 않고 업데이트하고 싶다면?
SWR
const Profile = () => {
const { mutate } = useSWRConfig();
const [profile, setProfile] = useState({ name: "jojiiiiyoung" });
const handleEditProfile = () => {
await updateProfile(profile);
// mutate 함수에 data 인자를 전달하면 refetch 없이 즉시 변경 가능합니다.
mutate(`/user/${userId}`, profile, false);
};
};
mutate함수에 키뿐 아니라, data를 전달하여 refetch없이 즉시 업데이트할 수 있다.
React Query
const Profile = () => {
const queryClient = useQueryClient();
const [profile, setProfile] = useState({ name: "jojiiiiyoung" });
const handleEditProfile = () => {
await updateProfile(profile);
// setQueryData를 통해서 데이터를 직접 변경할 수 있습니다!
queryClient.setQueryData(`/user/${userId}`, profile);
};
};
setQueryData라는 함수를 통해 직접 업데이트를 할 수 있다.
참고자료를 보면서 이해하니, 쉽게 이해가 갔다.