☑️ useMutation이란?
Mutation : 돌연변이, 형태나 구조상의 변화
React Query의 useMutation 훅은 데이터베이스에 새로운 값을 추가하거나 기존 데이터를 수정 및 삭제하는 등 "변경(Mutation)"이 필요한 작업을 수행할 때 사용한다. 이와 같은 작업은 사이드 이펙트를 발생시키며, 이러한 사이드 이펙트를 다루기 위해 useMutation을 사용한다.
✔️ useMutation()와 useQuery()의 차이
useQuery()의 쿼리 함수는 컴포넌트가 마운트될 때 자동으로 쿼리 함수를 실행해 데이터를 가져온다.useMutation()은 실제로 Mutation하는 함수를 직접 실행해 줘야 한다.mutate()함수를 통해mutationFn으로 등록했던 함수를 실행할 수 있고, 백엔드 데이터를 실제로 수정하게 된다.mutate()를 하면 백엔드 데이터는 변경되지만, 캐시에 저장된 데이터는 refetch를 하지 않는 이상 기존의 데이터가 그대로 저장되어있다.
☑️ useMutation()으로 데이터 추가하기
import { useState } from 'react';
import { useMutation, useQuery } from '@tanstack/react-query';
import { getPosts, uploadPost } from './api';
function HomePage() {
const [content, setContent] = useState('');
const {
data: postsData,
isPending,
isError,
} = useQuery({
queryKey: ['posts'],
queryFn: getPosts,
retry: 0,
});
const uploadPostMutation = useMutation({
mutationFn: (newPost) => uploadPost(newPost),
});
const handleInputChange = (e) => {
setContent(e.target.value);
}
const handleSubmit = (e) => {
e.preventDefault();
const newPost = { username: 'codeit', content };
uploadPostMutation.mutate(newPost);
setContent('');
};
if (isPending) return '로딩 중입니다...';
if (isError) return '에러가 발생했습니다.';
const posts = postsData?.results ?? [];
return (
<>
<div>
<form onSubmit={handleSubmit}>
<textarea
name="content"
value={content}
onChange={handleInputChange}
/>
<button disabled={!content} type="submit">
업로드
</button>
</form>
</div>
<div>
<ul>
{posts.map((post) => (
<li key={post.id}>
{post.user.name}: {post.content}
</li>
))}
</ul>
</div>
</>
);
}
export default HomePage;
위의 예제는 포스트 업로드 하는 기능을 만든 것이다. useMutation()을 작성하고, 업로드 버튼(handleSubmit)을 눌렀을 때mutate() 함수를 실행하도록 해줬다.
☑️ invalidateQueries() 데이터 바로 업데이트하기
useMutation() 훅을 이용해 새로운 데이터를 추가하지만, 캐시에 있는 데이터가 업데이트되지 않아서 확인하려면 새로고침을 해줘야한다. 이럴 때 쿼리 클라이언트의 invalidateQueries() 함수를 사용하면 업로드가 끝난 이후에 자동으로 refetch를 하도록 설정할 수 있다.
invalidateQueries()는 캐시에 있는 '모든 쿼리' 혹은 '특정 쿼리'를 invalidate(무효화하다)하는 함수이다. 쿼리를 invalidate하면 해당 쿼리를 통해 받아 온 데이터를 stale time에 상관없이 무조건 stale 상태로 만들고, 해당 데이터를 백그라운드에서 refetch하게 된다.
import { useQueryClient } from '@tanstack/react-query'
const queryClient = useQueryClient();
// ...
queryClient.invalidateQueries();
쿼리 클라이언트는 useQueryClient() 훅을 사용해서 가져올 수 있고, 원하는 시점에 queryClient.invalidateQueries() 함수를 실행하면 된다. 따라서 새로 데이터를 추가했을 때, 해당 쿼리를 invalidate하면 데이터를 자동으로 refetch할 수 있게되고, 새롭게 업로드 된 포스트도 바로 보여줄 수 있다.
☑️useMutation() 함수의 콜백 옵션
Mutation 객체는 onMutate, onSuccess, onError, onSettled 와 같은 주요 옵션들이 있어서 Mutation 사이클에 따라 적절한 동작을 추가할 수 있다. 아래 예제에선 onSuccess, 즉 뮤테이션이 성공한 시점에 ['post'] 쿼리를 invalidate해 주는 함수를 콜백으로 등록해주었다. 이처럼 코드를 추가하면 포스트를 업로드하자마자 업로드된 포스트까지 화면에 잘 보이게 된다.
const queryClient = useQueryClient();
// ...
const uploadPostMutation = useMutation({
mutationFn: (newPost) => uploadPost(newPost),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['posts'] });
},
});
✔️ mutate() 함수의 콜백 옵션
onSuccess, onError, onSettled 와 같은 옵션은 useMutation()에서 사용할 수 있고, mutate()함수에서도 사용할 수 있다. useMutation()에 등록한 콜백 함수들이 먼저 실행되고, 그 다음에 mutate()에 등록한 콜백 함수들이 실행된다.
const uploadPostMutation = useMutation({
mutationFn: (newPost) => uploadPost(newPost),
onSuccess: () => {
console.log('onSuccess in useMutation');
},
onSettled: () => {
console.log('onSettled in useMutation');
},
});
...
uploadPostMutation.mutate(newPost, {
onSuccess: () => {
console.log('onSuccess in mutate');
},
onSettled: () => {
console.log('onSettled in mutate');
},
});
💡주의할 점!
useMutation()에 등록된 콜백 함수들은 컴포넌트가 언마운트되더라도 실행이 되지만, mutate()의 콜백 함수들은 뮤테이션이 끝나기 전에 해당 컴포넌트가 언마운트되면 실행되지 않는 특징을 가지고 있다. 따라서 query invalidation과 같이 뮤테이션 과정에서 꼭 필요한 로직은 useMutation()을 통해 등록하고, 그 외에 다른 페이지로 리다이렉트한다든가, 혹은 결과를 토스트로 띄워주는 것과 같이 해당 컴포넌트에 종속적인 로직은 mutate()를 통해 등록해 주면 된다.
...
const uploadPostMutation = useMutation({
mutationFn: (newPost) => uploadPost(newPost),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['posts'] });
},
});
const handleUploadPost = (newPost) => {
uploadPostMutation.mutate(newPost, {
onSuccess: () => {
toast('포스트가 성공적으로 업로드 되었습니다!');
},
});
};
✔️ isPending 프로퍼티 활용하기
포스트가 업로드 중에는 중복해서 업로드를 하면 안되니까 뮤테이션에 isPending라는 값이 있으니, 위와 같이 uploadPostMutation.isPending 로 이용하여 버튼을 비활성화 할 수 있다.
<button disabled={uploadPostMutation.isPending || !content} type='submit'>업로드</button>'React' 카테고리의 다른 글
| [React] React-Query(2) - useQuery와 쿼리 상태, 캐싱, 로딩과 에러 처리 (2) | 2024.11.13 |
|---|---|
| [React] React-Query(1) - 소개 & 설치 (5) | 2024.11.08 |