[React] react-query 정리

참고


react-query api 모음

  • reat-query 에서 사용하는 api 들을 하나씩 정리 진행합니다.

useQuery

  • 데이터를 get 하기 위한 api입니다. post, update는 useMutation을 사용합니다.
  • 첫번째 파라미터로 unique Key가 들어가고, 두번째 파라미터로 비동기 함수(api호출 함수)가 들어갑니다.
  • 첫번째 파라미터로 설정한 unique Key는 다른 컴포넌트에서도 해당 키를 사용하면 호출 가능합니다. unique Key는 string과 배열을 받습니다. 배열로 넘기면 0번 값은 string값으로 다른 컴포넌트에서 부를 값이 들어가고 두번째 값을 넣으면 query 함수 내부에 파라미터로 해당 값이 전달됩니다.
  • return 값은 api의 성공, 실패여부, api return 값을 포함한 객체입니다.
  • useQuery는 비동기로 작동합니다. 즉, 한 컴포넌트에 여러개의 useQuery가 있다면 하나가 끝나고 다음 useQuery가 실행되는 것이 아닌 두개의 useQuery가 동시에 실행됩니다. 여러개의 비동기 query가 있다면 useQuery보다는 밑에 설명해 드릴 useQueries를 권유드립니다.

  • useQuery 사용 예제 코드입니다.
  • 우선, 저는 json-server 를 이용하여 가짜 서버에 json 형식으로 테이블 하나를 생성 및 데이터 추가를 해주었습니다.
{
    "posts": [
        {
            "id": "37f251ea-93ef-49bb-9642-f4b5c2ab3f51",
            "title": "BeomBeomJoJo",
            "body": "Body Content"
        }
    ]
}
  • 여기서 fetch api 를 이용하여 해당 데이터를 조회하는 함수를 추가해 주었습니다.
export async function fetchPosts() {
    const response = await fetch("http://localhost:3000/posts");
    return response.json();
}
  • 위 코드는 http://localhost:3000/posts 서버에 데이터를 요청하여 해당 데이터를 가져와 json 형식으로 return 해주는 함수입니다.
  • 이제 위 내용을 토대로, 처음 프로젝트가 시작할 때 서버의 데이터를 모두 조회하는 로직을 react-query 의 useQuery api 를 사용하여 구현해 보았습니다.
import { useQuery } from "@tanstack/react-query";
import { useParams } from "react-router-dom";
import { fetchPost } from "../api/posts";
import { useNavigate } from "react-router-dom";

export default function Post() {
    const navigate = useNavigate();
    const { id } = useParams();
    const {
        isLoading,
        isError,
        data: post,
        error,
    } = useQuery({
        queryKey: ["posts", id],
        queryFn: () => fetchPost(id),
        refetchOnWindowFocus: false, // react-query는 사용자가 사용하는 윈도우가 다른 곳을 갔다가 다시 화면으로 돌아오면 이 함수를 재실행합니다. 그 재실행 여부 옵션 입니다.
        retry: 0, //실패시 재호출 몇 번 진행하지에 대한 여부
        onSuccess: (post) => {
            // 성공시 호출
            console.log(post);
        },
        onError: (e) => {
            // 실패시 호출 (401, 404 같은 error가 아니라 정말 api 호출이 실패한 경우만 호출됩니다.)
            // 강제로 에러 발생시키려면 api단에서 throw Error 날립니다.
            console.log(e.message);
        },
    });

    if (isLoading) return "loading...";
    if (isError) return `Error: ${error.message}`;

    return (
        <div>
            <button
                onClick={() => {
                    navigate("/");
                }}
            >
                back to list posts
            </button>
            <h1>{post.title}</h1>
            <p>{post.author}</p>
        </div>
    );
}
  • 아래와 같이 json-server 의 데이터를 가져와서 출력해 주는 것을 볼 수 있습니다.


useQueries

  • useQuery 를 비동기로 여러 개 실행할 경우, 다음 코드와 같이 귀찮은 경우들이 발생하게 됩니다.
  • 아래 코드를 보게 되면, 세 함수 모두 비동기로 실행합니다.
  • 하지만, 세 변수를 개발자가 다 기억을 해야함과 동시에 세 변수에 대한 로딩, 성공, 실패처리를 모두 한다는 번거로운 작업이 생깁니다.
const usersQuery = useQuery("users", fetchUsers);
const teamsQuery = useQuery("teams", fetchTeams);
const projectsQuery = useQuery("projects", fetchProjects);
  • 이때 promise.all처럼 useQuery를 하나로 묶을 수 있는데, 그것이 useQueries입니다. promise.all과 마찬가지로 하나의 배열에 각 쿼리에 대한 상태 값이 객체로 들어옵니다.
// 아래 예시는 롤 룬과, 스펠을 받아오는 예시입니다.
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

  • 값을 바꿀 때 사용하는 API 입니다.
  • return 값은 useQUery 와 동일하고, 예시 코드를 통해 어떻게 사용 되는지 보여 드리도록 하겠습니다.
import PostForm from "../components/PostForm";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useParams } from "react-router-dom";
import { fetchPost, updatePost } from "../api/posts";
import { useNavigate } from "react-router-dom";

export default function EditPost() {
    const queryClient = useQueryClient();
    const navigate = useNavigate();
    const { id } = useParams();
    const {
        isLoading,
        isError,
        data: post,
        error,
    } = useQuery({
        queryKey: ["posts", id],
        queryFn: () => fetchPost(id),
    });
    const updatePostMutation = useMutation({
        mutationFn: updatePost,
        onSuccess: () => {
            // react-query 장점으로, 데이터 update 후에 get 함수를 간단히 재실행 할 수 있습니다.
            // mutation 함수가 성공했다면, unique key 로 맵핑된 get 함수를 invalidateQueries 에 넣어주면 됩니다.
            queryClient.invalidateQueries({ queryKey: ["posts"] });
            navigate("/");
        },
    });

    if (isLoading) return "loading...";
    if (isError) return `Error: ${error.message}`;

    const handleSubmit = (updatePost) => {
        updatePostMutation.mutate({ id, ...updatePost });
    };

    return (
        <div>
            <PostForm onSubmit={handleSubmit} initialValue={post} />
        </div>
    );
}
  • 위에서 보면, useMutation 을 이용하여 데이터를 Update 하는 것을 확인하실 수 있습니다.
  • 여기서 중요한 부분은, invalidateQueries 함수 입니다.
  • 이 함수를 통해 mutation 함수가 성공 했다면, unique key 로 맵핑된 get 함수를 다시 재실행 시켜줘서, 업데이트 된 내용이 바로 재렌더링 되어 반영되게 할 수 있습니다.
  • 이처럼, 데이터 변경 작업 혹은 추가 작업은 useMutation 함수를 이용하면 됩니다.
728x90

이 글을 공유하기

댓글

Designed by JB FACTORY