출처
React Query Tutorial for Beginners
https://www.youtube.com/playlist?list=PLC3y8-rFHvwjTELCrPrcZlo6blLBUspd2
리액트에서 데이터를 fetching할 때 사용하는 라이브러리
리액트는 UI 라이브러리 따라서 데이터를 패칭할 때 특정 패턴을 가지고 있지 않음
보통 데이터를 패칭할 때 useEffect를, 컴포넌트 state를 유지(로딩, 에러 결과)할 때 useState를 사용함
그리고 만약 데이터가 여러곳에서 필요하다면 state management 라이브러리를 사용함
대부분의 state management 라이브러리는 client state와 잘 작동함 그러나 비동기 또는 server state와는 잘 맞지 않음
(state management libraries are not great for working with asynchronous or server state)
→ client state와 server state가 매우 다르기 때문!
: 앱 메모리에 유지되며 동기적으로 접근, 업데이트함
: 원격으로 유지되며 접근 또는 업데이트를 위해 비동기 API가 필요함
challenging when you have to deal with caching, deduping multiple requests for the same data, updating stale data in the background, performance optimizations etc
이는 특히 캐싱, 동일한 데이터에 대한 중복 요청을 제거할 때, background의 오래된 데이터를 업데이트 할 때, 성능 최적화를 처리할 때 등 어려울 수 있음
이걸 일일히 하려면 많은 시간과 노력이 필요함 따라서 다 해주는 라이브러리를 통해 시간과 노력을 아끼자!
step 1) CRA로 새로운 리액트 프로젝트를 만든다
step 2) 앱에서 사용할 모의 데이터를 제공하는 API 엔드포인트 설정
step 3) 리액트 라우터와 application routes 설정
step 4) 전통적인 방법인 useEffect, useState로 데이터 패칭(비교용!)
https://github.com/gopinav/React-Query-Tutorials 여기서 클론해서 사용하면 된다고 함
나는 기존에 하던 프로젝트가 있어서 거기에 적용해보려고 한다
만약 이 튜토리얼을 따라 만들고 싶다면
npm i json-server or yarn add json-server
그리고 프로젝트 root에
db.json
파일을 만들어준다 그리고 여기에 api 데이터로 쓸 데이터를 넣어주면 됨
마지막으로 package.json의 scripts에 "serve-json" : "json-server --watch db.json --port 4000"
을 입력하면 끝이다
이제 터미널에 yarn serve-json
을 입력하면 로컬호스트 포트 4000에서 api endpoint data를 확인할 수 있다
step2, 3은 생략 자세한건 영상을 참조하길 👍
코드가 긴 관계로 관련 없는 부분은 생략했다
//... import 생략
const Todos = () => {
const token = localStorage.getItem("token") || "";
const [isLoading, setIsLoading] = useState<boolean>(true);
const [list, setList] = useState<IList[]>([]);
const navigate = useNavigate();
useEffect(() => {
ToDoAPI.getTodos(token).then((res) => {
setList(res.data.data);
setIsLoading(false);
});
}, [token]);
if (isLoading) {
return <h2>Loading...</h2>;
}
return (
<Container id="top">
<Content>
<section>
{list.map((list, idx) => (
<TodoList
list={list}
key={idx}
id={list.id}
/>
))}
</section>
</Content>
</Container>
);
};
먼저 root component인 App.tsx에서 설정을 해줘야 한다
// ... import 생략
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; // 리액트 쿼리의 개발도구
const querClient = new QueryClient();
function App() {
return (
<React.StrictMode>
<QueryClientProvider client={querClient}>
<BrowserRouter>
<Routes>
// routes 생략
</Routes>
</BrowserRouter>
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
</React.StrictMode>
);
}
export default App;
이제 react query가 제공하는 모든 훅과 기능을 사용할 수 있다
Todos.tsx
import { useQuery } from "@tanstack/react-query"; //1
const Todos = () => {
const token = localStorage.getItem("token") || "";
// 나는 리액트 쿼리를 사용하는 부분을 분리해주었다
// 이 부분은 원티드 챌린지 코치님 코드를 참조함
const Keys = {
all: ["todos"] as const,
details: () => [...Keys.all, "detail"] as const,
detail: (id: string) => [...Keys.details(), id] as const,
};
// destructuring
const { isLoading, data } = useQuery(
Keys.all,
() => ToDoAPI.getTodos(token).then((response) => response.data));
// loading
if (isLoading) {
return <h2>Loading...</h2>;
}
return (
<Container id="top">
<Content>
<section>
{data ? (
data?.data.map((list: IList, idx: number) => (
<TodoList
list={list}
key={idx}
id={list.id}
/>
))
) : (
<div>todo가 없습니다!</div>
)}
</section>
</Content>
</Container>
);
};
useQuery는 2개의 argument(매개변수)를 필요로한다
이렇게 리액트 쿼리를 사용하면 useEffect, useState 없이 구현할 수 있다
전통적인 방법
//... import 생략
const Todos = () => {
const token = localStorage.getItem("token") || "";
const [isLoading, setIsLoading] = useState<boolean>(true);
const [error, setError] = useState<string>("");
const [list, setList] = useState<IList[]>([]);
useEffect(() => {
ToDoAPI.getTodos(token).then((res) => {
setList(res.data.data);
setIsLoading(false);
})
.catch((error) => {
setError(error.message);
setIsLoadding(false);
});
}, [token]);
if (isLoading) {
return <h2>Loading...</h2>;
}
if (error) {
return <h2>{error}</h2>;
}
// 생략
};
import { useQuery } from "@tanstack/react-query";
const Todos = () => {
const token = localStorage.getItem("token") || "";
const Keys = {
all: ["todos"] as const,
details: () => [...Keys.all, "detail"] as const,
detail: (id: string) => [...Keys.details(), id] as const,
};
const { isLoading, data, isError } = useQuery(
Keys.all,
() => ToDoAPI.getTodos(token).then((response) => response.data));
// loading
if (isLoading) {
return <h2>Loading...</h2>;
}
if (isError) {
return <h2>error occur</h2>;
}
return (
// 생략
};
어려울줄 알았는데 여기까진 생각보다 간단했다
다음엔 추가 수정 삭제도 구현 ㄱ