본문 바로가기

신입개발자 멘붕극복스토리

React-query 사용하기

react-query 공식 홈페이지

 

React Query

Hooks for fetching, caching and updating asynchronous data in React

react-query.tanstack.com

리액트 쿼리로 서버 상태 관리하기

 

Overview

React Query makes

  • fetching,
  • caching,
  • synchronizing and
  • updating

server state in your React application a breeze.

 

 

React Query는 React app에서 비동기 로직을 좀 더 쉽게 다루게 해 주는 라이브러리이다.

 

 

 

 

 


Motivation

왜 이런 기능이 필요하게 되었을까

 

 

1. React는 기본적으로 data fetching, updating 기능을 제공하지 않는다.

Out of the box, React applications do not come with an opinionated way of fetching or updating data from your components so developers end up building their own ways of fetching data.

 

React는 기본적으로 data fetching, updating 기능을 제공하지 않는다.

그 때문에 app에서 비동기적으로 데이터를 전송하고 저장하기 위해서는 React hooks 와 같은 다른 라이브러리를 사용해야 한다.

 

 

 

2. 대부분의 상태 관리 라이브러리들이 client state에는 잘 작동하지만 server state에선 그렇지 않다.

While most traditional state management libraries are great for working with client state, they are not so great at working with async or server state.

This is because server state is totally different. For starters, server state:

  • Is persisted remotely in a location you do not control or own
  • Requires asynchronous APIs for fetching and updating
  • Implies shared ownership and can be changed by other people without your knowledge
  • Can potentially become "out of date" in your applications if you're not careful

 

그리고 대부분의 상태 관리 라이브러리들이 client state에는 잘 작동하지만 server state에선 그렇지 않다.

server state는 client state와는 완전히 다르기 때문인데, server state는 다음과 같은 특징이 있다.

  • 원격 공간에 저장되어 앱에서 컨트롤하지 않는다.
  • fetching이나 updating에 비동기 api가 필요하다.
  • 오너쉽을 공유하기 때문에 내가 모르는 사이 다른 사람에 의해 변경될 수 있다.
  • 주의를 기울이지 않으면 app에 있는 데이터가 잠재적으로 '기한이 만료된' 데이터가 될 수 있다.

 

 

  • client-state의 예)
    • React Component의 state
    • Redux store의 데이터
  • server-state의 예)
    • React app에서 비동기 요청으로 받아올 수 있는, backend DB에 저장되어 있는 데이터

 

 

 

 


The reason to use

사용하면 어떤 점이 좋을까

 

 

비동기 관련 boilerplate를 최소화할 수 있다.

보통 React에서는 Redux 와 같은 상태 관리 라이브러리를 사용하며 그것으로 server state를 관리하는 방식이 일반적이다.

Redux를 사용하면 thunk, saga  등의 미들웨어를 사용해, 서버 데이터 요청 액션이 들어오면 api를 호출하여 Redux state를 업데이트한다.

 

하지만 React Query는 기본적으로 함수형 컴포넌트 안에서 hook 형태로 사용하며 굳이 server state를 다른 장소에 저장할 필요가 없다.

const { data: speed, isLoading } = useQuery("speed", () => 
	axios.get("/api/speed")
);

 

 

 

 


How to use

Summary

  • Install
  • Import
  • Create a client
  • Provide the client to your app
  • Access the client
  • Subscribe to a query in your compnents or custom hooks : 데이터 가져오기 및 캐싱
  • Mutation  : 데이터 생성, 업데이트, 삭제
  • Invalidation : 쿼리 무효화

 

 

 

 

Install

npm i react-query

 

 

Import 

import {
	useQuery,
    useMutation,
    useQueryClient,
    QueryClient,
    QueryClientProvider
 } from 'react-query'

 

 

 

Create a client

const queryClient = new QueryClient();

 

 

 

Provide the client to you app

function App(){
	return (
    	<QueryClientProvider client={queryClient}>  //queryClient : interact with a cache
        	<Todo/>
        </QueryClientProvider>
    )
};

 

  • Component가 hook 안에서 QueryClient 인스턴스에 접근할 수 있도록 컴포넌트 트리 최상위에 QueryClientProvider를 추가한다.

       -> 사용하고자 하는 컴포넌트를 QueryClientProvider로 감싸고 queryClient를 props로 내려줘야 한다.
            (비동기 요청을 알아서 처리하도록 해 준다.)

 

 

 

 

 

Access the client

function Todos() {
	const queryClient = useQueryclient()

QueryClient 인스턴스를 반환한다.

 

 

 

 

Subscribe to a query in your compoents or custom hooks

const query = useQuery('todos', getTodos)

 

  • 쿼리는 서버에 데이터를 패치할 때 비동기 기반의 어떤 메소드와도 사용할 수 있다.

        (데이터를 수정할 때에는 Mutation 을 사용할 수 있다.)

 

 

useQuery

서버에서 데이터를 가져오고 캐싱을 할 때 사용하는 hook이다.

const result = useQuery('toDos', fetchTodoList);
  • const { data, isLoading } = useQuery( queryKey, queryFunction, options)
  • queryKey : 문자열이나 배열
  • queryFunction : 서버에 데이터를 요청하고, promise를 리턴하는 함수를 전달한다.
  • Return Data:
    • data - 쿼리 함수가 리턴한 promise에서 resolve된 데이터
    • isLoading - 저장된 캐시가 없는 상태에서 데이터를 요청 중일 때 true를 리턴한다.
    • isFetching - 캐시가 있든 없든 데이터가 요청 중일 때 true를 리턴한다.

 

 

 

 

 

Mutation

function togoMutation = useMutation(
	newTodo => axios.post('/todos', newTodo),
    { 
    onSuccess: (data) => { ... },
    onError: (error) => { ... }
    },
);

 

데이터를 생성, 수정, 삭제 할 때 , 혹은 서버 side-effect를 위해 사용하는 hook이다.

useQuery 옵션처럼 onSuccess, onError, onSettled 콜백을 전달할 수 있으며 mutate를 호출했을 때 실행할 onMutate 콜백도 사용할 수 있다.

 

 

 

 

 

Invalidation

const queryClient = useQueryClient();

queryClient.invalidateQueries(); // 캐시에 있는 모든 쿼리를 무효화한다.
queryClient.invalidateQueries('todos');  // todos로 시작하는 키를 가진 모든 쿼리를 무효화한다.

useQueryClient

캐시에 있는 QueryClient 인스턴스를 반환한다.

 

 

invalidateQueries

Waiting for queries to become stale before they are fetched again doesn't always work, especially when you know for a fact that a query's data is out of date because of something the user has done. For that purpose, the QueryClient has an invalidateQueries method that lets you intelligently mark queries as stale and potentially refetch them too.

 

데이터가 다시 패치되기 전에 그 상태가 stale 상태로 바뀌기만 기다릴 필요가 없는 경우도 있다. 특히 사용자가 어떤 작업을 끝냈고 그 때문에 query 데이터가 이미 만료되었음을 인지했을 때가 이에 해당한다.

invalidateQueries 메소드를 사용하면 query를 stale상태로 표시하고 refetch할 수 있게 된다.