react-query

지리·2022년 11월 27일
0

React Query

Server StateClient State의 분리를 목적으로 만들어짐
공식 문서

react-query 도입 이유

react-query를 사용하기 전 axios로 api를 요청하는 함수만 만들어놓고 컴포넌트에서 불러오는 방식을 사용하고 있었다. 물론 전역적인 상태관리도 안되고 있었기 때문에 redux사용에 대한 고민을 많이 했었는데 현재 만드는 앱의 규모에서 redux를 사용하기에는 작성되는 코드의 양에 비해 필요성이 느껴지지 않았다. (물론 이건 내가 redux를 제대로 사용할줄 몰라서 그런걸수도 있다...) 고민하던 중에 react-query를 알게 되고 더 간편하게 캐싱이나 전역관리가 될수 있을 것 같아 도입하게 되었다.

컴포넌트 안에 API를 요청할때의 문제점

  • 캐싱(유지)되지 못하고 사용자가 컴포넌트를 나갈때 데이터가 사라지며 다시 돌아왔을경우 또 다시 데이터로딩을 해야한다.

  • 캐싱을 원한다면 데이터를 Context 또는 리덕스, 리코일 등의 라이브러리에서 관리해야 하는데 이렇게 되면 준비해야 할 코드가 많다(redux-thunk, redux-saga등 미들웨어사용)

    -> 리액트 쿼리를 사용하면 컴포넌트에서 Hook을 기반으로 데이터 로딩을 훨씬 편하게 할 수 있고, 캐싱도 기본적으로 제공하여 쉽게 구현할 수 있다.

사용법

useQuery

import {useQuery} from 'react-query';

function Sample(){
	const result = useQuery('articles',getArticles);
	const {data,error,isLoading} = result;
}

//isLoading : loading상태 true인지 false인지
//data : 요청 성공한 데이터
  • userQuery의 첫번째 인자에는 조회하고 싶은 데이터의 를 넣는다.
  • 한번 받아온 데이터의 경우 이 가 같을경우 기존의 데이터를 보여줌

  • 키는 배열,객체,문자열 이 가능한데 배열의 경우 순서가 중요, 객체에서는 순서는 상관없다
  • 문자열로 넣을경우 ['문자열']과 같음

queryKey를 배열로 넣을경우 queryFn에서 prameter로 받을 수 있다
unique key 활용


useQueries

  • useQueries를 이용하면 여러개의 비동기 useQuery를 한번에 가져올 수 있다.
  • 동기적으로 작동하며, 앞서 가져온 데이터에 다음으로 가져올 쿼리가 영향을 받을경우 쓰면 좋음

const result = useQueries([
  {
    queryKey: ["getRune", riot.version],
    queryFn: () => api.getRunInfo(riot.version)
  },
  {
    queryKey: ["getSpell", riot.version],
    queryFn: () => api.getSpellInfo(riot.version)
  }
]);

useEffect(() => {
  console.log(result); 
  // [{rune 정보, data: [], isSucces: true ...}, {spell 정보, data: [], isSucces: true ...}]
  const loadingFinishAll = result.some(result => result.isLoading);
  console.log(loadingFinishAll); // loadingFinishAll이 false이면 최종 완료
}, [result]);

useMutation

서버에서 온 데이터를 수정해야 한다면 mutation을 추천
react query 사용법 및 쓰는 이유(블로그)

useMutation후 쿼리 데이터 업데이트 방법 두가지

1. 쿼리 데이터 무효화로 리랜더링 실행 (Invalidation)
Invalidation
invalidateQueries matching

 import { useMutation, useQueryClient } from 'react-query'

 const queryClient = useQueryClient()

// When this mutation succeeds, invalidate any queries with the `todos` or `reminders` query key

 const mutation = useMutation(addTodo, {

 onSuccess: () => {

     queryClient.invalidateQueries('todos')

     queryClient.invalidateQueries('reminders')

 },

 })

2. optimistic update
optimistic update란 서버와의 통신이 실패하던지 성공하던지 낙관적으로 바라보고 성공을 가정하고 쿼리데이터를 업데이트 하는것

const queryClient = useQueryClient()


 useMutation(updateTodo, {

 // When mutate is called:
 onMutate: async newTodo => {
 
 // overwrite하지 않게 기존 쿼리 데이터는 취소 한다
 await queryClient.cancelQueries('todos')
 
 // 이전 쿼리 데이터를 저장(서버 통신에 실패할경우 이 데이터로 돌리기 위해)
 const previousTodos = queryClient.getQueryData('todos')

 // 새 값으로 쿼리 업테이트
 queryClient.setQueryData('todos', old => [...old, newTodo])

 // 이전 쿼리 값은 return한다
 return { previousTodos }

 },

 // 만약 mutation에 실패하면 이전 쿼리 데이터로 rollback
 onError: (err, newTodo, context) => {
     queryClient.setQueryData('todos', context.previousTodos)
 },

 // error나 success일때 항상 쿼리데이터를 referch
 onSettled: () => {
     queryClient.invalidateQueries('todos')
 },

 })
  • queryClient.cancelQuerie : 발신 쿼리를 취소하는 데 사용할 수 있다. (optimistic update를 덮어쓰지 않도록)
  • queryClient.getQueryData : 기존 쿼리의 상태를 가져오는 동기 함수. 쿼리가 존재하지 않으면 undefined 를 반환.
  • queryClient.setQueryData : 쿼리의 캐시된 데이터를 즉시 업데이트할 수 있는 동기 함수. 쿼리가 존재하지 않으면 생성된다.

useInfiniteQuery

무한 스크롤 이용시 유용한 hook

useQuery pagenation과 다른점

  • 자동으로 연결되어 데이터를 반환해줌
  • useQuery의 keepPreviousData를 true로 한다면 쿼리 키가 변경되었더라도 새 데이터가 요청되는 동안 마지막으로 성공적으로 가져온 데이터를 사용할 수 있다.
  • 새 데이터가 도착하면 이전 데이터가 원활하게 스왑되어 새 데이터를 표시한다.

사용법

useInfiniteQuery(key, queryFn, { getNextPageParam : 값이 undefined가 되기 전까지 fetch })

data - 무한 쿼리데이터를 포함하는 개체

  • data.pages - 가져온 페이지를 포함하는 배열

데이터를 가져오면 data.pages의 배열 마지막에 가져와 진다. 즉 객체 하나의 데이터를 가져오는게 아니라면 [{results:[array],...},{result:[array]},...]이 되어 버림

-> 해결방안 : flatMap을 이용하면 배열속의 값들을 하나의 배열로 다시 만들 수 있다.
flatMap
참고한 stackOverFlow

  • data.pageParams - 데이터를 가져오는데 사용한 페이지 매개변수를 포함하는 배열
const getNextPageParam = (lastPage, pages)=>{
	const nextPage = pages.length
	return lastPage.length < GET_SIZE ? undefined : nextPage
}
  • fetchNextPage - getNextPageParam을 넣어 data를 가져오게 하는 함수
<button onClick={() => fetchNextPage()}>

Infinite query refech

infinite query가 invalidate될때 어떻게 작동하는지
: ## What happens when an infinite query needs to be refetched?

해결사항

redux를 이용하는 것과 달리 reatQuery이용시 어디에서 api를 부르는지 모를수 있음... (컴포넌트에 유착되는 등) 이에 대한 해결책 필요...


참고
# Store에서 비동기 통신 분리하기 (feat. React Query)
공식문서 번역 블로그
https://kyounghwan01.github.io/blog/React/react-query/basic/#%E1%84%89%E1%85%A1%E1%84%8B%E1%85%AD%E1%86%BC%E1%84%92%E1%85%A1%E1%84%82%E1%85%B3%E1%86%AB-%E1%84%8B%E1%85%B5%E1%84%8B%E1%85%B2

profile
공부한것들, 경험한 것들을 기록하려 노력합니다✨

0개의 댓글