무엇
useMutationState는 MutationCache에 있는 뮤테이션들을 읽어오는 훅이다. 조건으로 거르고, 각 뮤테이션에서 원하는 값만 골라 배열로 받는다.
인자는 객체 하나다. filters로 어떤 뮤테이션을 볼지 좁히고, select로 각 뮤테이션에서 뽑을 값을 정한다. 반환은 그 결과들의 배열이다.
무슨 일이 일어나나
mutate를 부를 때마다 MutationCache에 항목이 하나씩 쌓이고, 그 항목은 gcTime 동안 남아 있다. useMutationState는 이 캐시를 구독해, filters에 맞는 항목들을 찾아 select를 적용한 값들을 배열로 돌려준다. 매칭되는 뮤테이션이 여러 개일 수 있어 배열이다. 가장 최근 것은 data[data.length - 1]로 꺼낸다.
사용법
filters에 { status: 'pending' }을 주면 지금 실행 중인 뮤테이션만 걸러진다. select로 각 뮤테이션의 state에서 필요한 조각(variables, data 등)을 뽑는다.
import { useMutationState } from '@tanstack/react-query'
// 실행 중인 뮤테이션들이 보낸 변수만 모은다
const variables = useMutationState({
filters: { status: 'pending' },
select: (mutation) => mutation.state.variables,
})
mutationKey로 특정 뮤테이션 계열만 볼 수도 있다.
const data = useMutationState({
filters: { mutationKey: ['posts'] },
select: (mutation) => mutation.state.data,
})
실무 예시
낙관적 목록에 흔히 쓴다. 새 글을 올리는 뮤테이션이 진행 중일 때, 아직 서버 응답 전인 그 글을 목록 끝에 미리 보여주는 경우다. useMutationState로 pending 뮤테이션의 variables를 가져와 흐리게 렌더한다.
import { useMutationState } from '@tanstack/react-query'
export function PendingPosts() {
const pendingTitles = useMutationState({
filters: { mutationKey: ['posts'], status: 'pending' },
select: (m) => (m.state.variables as { title: string }).title,
})
return (
<ul>
{pendingTitles.map((title, i) => (
<li key={i} style={{ opacity: 0.5 }}>
{title} (저장 중)
</li>
))}
</ul>
)
}
왜 중요한가
한 컴포넌트에서 useMutation을 부르고 그 결과를 바로 쓰는 게 기본이다. useMutationState는 그 범위를 넘어, 뮤테이션을 일으킨 곳과 상태를 보여줄 곳이 떨어져 있을 때 다리를 놓는다. 낙관적 UI를 여러 컴포넌트에 걸쳐 만들거나, 진행 중인 작업을 한데 모아 보여줄 때 쓴다. 자주는 아니어도 그런 순간엔 대체재가 마땅치 않다.
Reference