Search
Duplicate

[React] React-Query로 리팩토링하기

간단소개
팔만코딩경 컨트리뷰터
ContributorNotionAccount
주제 / 분류
Scrap
태그
9 more properties

계속 작성중입니다.

React Query

React Query는 …

Installation

npm install react-query # or yarn add react-query
Shell
복사

setup

// index.js import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; import { QueryClient, QueryClientProvider } from 'react-query'; import { ReactQueryDevtools } from 'react-query/devtools'; const queryClient = new QueryClient(); const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <QueryClientProvider client={queryClient}> {/* devtools */} <ReactQueryDevtools initialIsOpen={true} /> <App /> </QueryClientProvider> </React.StrictMode> );
TypeScript
복사

React Query 사용해보기

useQuery

기존코드
import { useState } from 'react'; import todoController from 'src/api/todoController'; import { TodoResponse } from 'src/types/TodoResponse'; const useTodo = () => { const [todos, setTodos] = useState<TodoResponse[]>([]); const [isLoading, setIsLoading] = useState(false); const [isError, setIsError] = useState(false); const handleFetchTodos = (fetchedTodos: TodoResponse[]) => { setTodos( fetchedTodos.sort( (a, b) => Number(new Date(b.createdAt)) - Number(new Date(a.createdAt)) ) ); }; const handleTodoError = (error: Error) => { alert(error); setIsError(true); }; const getTodos = () => { setIsLoading(true); todoController .getTodos() .then(handleFetchTodos) .catch(handleTodoError) .finally(() => setIsLoading(false)); }; return todos, isLoading, isError, getTodos, }; }; export default useTodo;
TypeScript
복사
수정한 코드
import { useEffect, useState } from 'react'; import todoController from 'src/api/todoController'; import { TodoResponse } from 'src/types/Todo'; import { useQuery, useQueryClient } from 'react-query'; const useTodo = () => { const queryClient = useQueryClient(); const [sortedTodos, setSortedTodos] = useState<TodoResponse[]>([]); const { data, isError, isLoading } = useQuery( 'todos', todoController.getTodos ); const sortByUpdatedAt = (todos: TodoResponse[]) => todos.sort( (a, b) => Number(new Date(b.updatedAt)) - Number(new Date(a.updatedAt)) ); useEffect(() => { if (data) setSortedTodos(() => sortByUpdatedAt(data)); }, [data]); return { isError, isLoading, sortedTodo }; }; export default useTodo;
TypeScript
복사

useMutation

React Query 공식문서에서는 POST와 같이 데이터를 바꾸는 요청에서는 useMutation을 사용할 것을 권장합니다.
1.
업데이트 후, 쿼리를 무효화시킨 후에 재요청하여 모든 데이터를 다시 가져오는 방법
2.
업데이트 후, 업데이트 된 데이터만을 추가/수정/삭제 하는 방법
3.
Optimistic Update(낙관적 업데이트)

1. 업데이트 후, 쿼리를 무효화시킨 후에 재요청하여 모든 데이터를 다시 가져오는 방법

여기서 유심히 봐야할 것은 useMutation입니다. useMutation은 useQuery와는 다르게 query key를 첫 번째 인자로 갖지 않습니다. 대신 첫 번째 인자로 query function이 들어가지요. 그리고 두 번째 인자로 옵션이 들어갑니다. 여기서는 onSuccess 메서드가 들어갔습니다. 요청이 성공한 후에 실행되는 부분입니다. onSuccess 함수의 내부에 queryClient.invalidateQueries("todos") 가 있습니다. 이것이 의미하는 것은 query function인 addTodo 함수가 호출되고 요청이 성공적이었으면, "todos" query key를 갖는 쿼리의 데이터를 무효화시키겠다는 것입니다. 데이터를 무효화시키게 될 경우 refetch(재요청)가 일어나고 그로 인해 UI가 업데이트 되는 것을 확인할 수 있습니다. useMutation이 반환하는 값 중 mutate 함수가 있는데요. 이 함수가 query function을 호출하는 역할을 합니다. 폼 제출시 서버에 데이터를 전송하도록 onSubmit 안에서 호출되고 있습니다. 이제 결과를 확인하기 위해서 코드를 실행시켜보겠습니다.

2. 업데이트 후, 업데이트 된 데이터만을 추가/수정/삭제 하는 방법

3. Optimistic Update(낙관적 업데이트)

import { useEffect, useState } from 'react'; import todoController from 'src/api/todoController'; import { createDummyTodo } from 'src/utils/todoDummy'; import { TodoResponse, CreateTodo, UpdateTodo, MutateTodo, RemoveTodo } from 'src/types/Todo'; import { useQuery, useMutation, useQueryClient } from 'react-query'; import { OptimisticFunction, create, update, remove } from 'src/utils/OptimisticFunction'; const useTodo = () => { const queryClient = useQueryClient(); const [sortedTodos, setSortedTodos] = useState<TodoResponse[]>([]); const { data, isError, isLoading } = useQuery('todos', todoController.getTodos); const handleRefetching = () => queryClient.invalidateQueries('todos'); const handleMutation = async (mutateTodo: MutateTodo, optimisticFunction: OptimisticFunction) => { await queryClient.cancelQueries('todos'); const dummyTodo = createDummyTodo(mutateTodo); const previousTodos = queryClient.getQueryData<TodoResponse[]>('todos'); queryClient.setQueryData<TodoResponse[]>('todos', (_previousTodos) => optimisticFunction(_previousTodos, dummyTodo) ); return { previousTodos }; }; const { mutate: createTodo } = useMutation((createTodo: CreateTodo) => todoController.createTodo(createTodo), { onMutate: (createTodo) => handleMutation(createTodo, create), onError: (_error, _newTodo, context) => { queryClient.setQueryData('todos', context?.previousTodos); }, onSettled: handleRefetching, }); const { mutate: updateTodos } = useMutation((updateTodos: UpdateTodo) => todoController.updateTodos(updateTodos), { onMutate: (updateTodos) => handleMutation(updateTodos, update), onError: (_error, _newTodo, context) => { queryClient.setQueryData('todos', context?.previousTodos); }, onSettled: handleRefetching, }); const { mutate: removeTodo } = useMutation((removeTodo: RemoveTodo) => todoController.removeTodo(removeTodo), { onMutate: (removeTodo) => handleMutation(removeTodo, remove), onError: (_error, _newTodo, context) => { queryClient.setQueryData('todos', context?.previousTodos); }, onSettled: handleRefetching, }); const sortByUpdatedAt = (todos: TodoResponse[]) => todos.sort((a, b) => Number(new Date(b.updatedAt)) - Number(new Date(a.updatedAt))); useEffect(() => { if (data) setSortedTodos(() => sortByUpdatedAt(data)); }, [data]); return { isError, isLoading, sortedTodos, createTodo, updateTodos, removeTodo, }; }; export default useTodo;
TypeScript
복사

성능비교

Axios 단일 사용

Axios + React-Query

Fetch + React-Query

Reference