무엇
QueryErrorResetBoundary는 그 경계 안에서 발생한 쿼리 에러를 초기화할 수 있게 해 주는 컴포넌트다. throwOnError나 Suspense로 에러를 위로 던지는 쿼리에서 "다시 시도" 버튼을 만들 때 쓴다.
함수를 자식으로 받는다. 그 함수는 reset을 받아, 호출하면 경계 안 쿼리들의 에러 상태를 지운다.
무슨 일이 일어나나
throwOnError(또는 Suspense)를 켠 쿼리는 실패하면 에러를 렌더 중에 던지고, 그걸 상위 ErrorBoundary가 잡는다. 문제는 그다음이다. 그냥 두면 에러 상태가 남아, 다시 렌더해도 곧바로 또 던진다. QueryErrorResetBoundary가 주는 reset을 부르면 그 에러 상태가 지워져, 다음 렌더에서 쿼리가 새로 재시도된다.
즉 이 컴포넌트 혼자 UI를 그리지는 않는다. React의 ErrorBoundary가 화면을 담당하고, QueryErrorResetBoundary는 그 둘을 잇는 reset을 넘겨줄 뿐이다.
사용법
자식으로 함수를 넣고, 인자에서 reset을 꺼낸다. react-error-boundary의 ErrorBoundary와 짝지어, onReset에 reset을 연결한다.
import { QueryErrorResetBoundary } from '@tanstack/react-query'
import { ErrorBoundary } from 'react-error-boundary'
const App = () => (
<QueryErrorResetBoundary>
{({ reset }) => (
<ErrorBoundary
onReset={reset}
fallbackRender={({ resetErrorBoundary }) => (
<div>
문제가 생겼다
<button onClick={() => resetErrorBoundary()}>다시 시도</button>
</div>
)}
>
<Page />
</ErrorBoundary>
)}
</QueryErrorResetBoundary>
)
버튼이 resetErrorBoundary를 부르면 ErrorBoundary의 onReset이 돌고, 거기 연결된 reset이 쿼리 에러를 지운다. 그러면 Page가 다시 렌더되며 쿼리가 재시도된다.
실무 예시
Suspense와 함께 쓰는 상세 화면이다. 로딩은 Suspense가, 실패와 재시도는 이 조합이 맡는다. 사용자가 "다시 시도"를 누르면 fallback이 사라지고 쿼리가 처음부터 다시 돈다.
import { Suspense } from 'react'
import { QueryErrorResetBoundary } from '@tanstack/react-query'
import { ErrorBoundary } from 'react-error-boundary'
import { Profile } from './Profile'
export function ProfileScreen() {
return (
<QueryErrorResetBoundary>
{({ reset }) => (
<ErrorBoundary
onReset={reset}
fallbackRender={({ resetErrorBoundary }) => (
<button onClick={resetErrorBoundary}>다시 불러오기</button>
)}
>
<Suspense fallback={<p>불러오는 중…</p>}>
<Profile />
</Suspense>
</ErrorBoundary>
)}
</QueryErrorResetBoundary>
)
}
왜 중요한가
Suspense나 throwOnError로 에러를 ErrorBoundary에 넘기는 방식을 택했다면, 재시도를 붙이는 순간 이 컴포넌트가 필요해진다. 없으면 ErrorBoundary를 리셋해도 쿼리 에러가 남아 화면이 다시 깨진다. 선언형 에러 처리를 쓰는 코드베이스에서 "다시 시도"를 제대로 동작시키는 잃어버린 조각 같은 존재다. 반대로 에러를 값으로 다루는 방식이면 만날 일이 없다.
Reference