본문으로 건너뛰기

React Query 4로 마이그레이션하기

호환성을 깨는 변경 사항

v4는 메이저 버전이므로 알아두어야 할 몇 가지 호환성을 깨는 변경 사항이 있습니다:

react-query는 이제 @tanstack/react-query입니다

종속성을 제거/설치하고 import를 변경해야 합니다:

npm uninstall react-query
npm install @tanstack/react-query
npm install @tanstack/react-query-devtools
- import { useQuery } from 'react-query' // [!code --]
- import { ReactQueryDevtools } from 'react-query/devtools' // [!code --]

+ import { useQuery } from '@tanstack/react-query' // [!code ++]
+ import { ReactQueryDevtools } from '@tanstack/react-query-devtools' // [!code ++]

Codemod

가져오기 마이그레이션을 더 쉽게 하기 위해 v4에는 codemod가 포함되어 있습니다.

codemod는 호환성을 깨뜨리는 변경 사항의 마이그레이션을 돕기 위해 최선을 다합니다. 생성된 코드를 철저히 검토해 주세요! 또한 codemod로 찾을 수 없는 예외 사례도 있으므로 로그 출력을 주의 깊게 살펴보세요.

다음 명령 중 하나(또는 둘 다)를 사용하여 쉽게 적용할 수 있습니다:

.js 또는 .jsx 파일을 대상으로 실행하려면 아래 명령을 사용하세요:

npx jscodeshift ./path/to/src/ \
--extensions=js,jsx \
--transform=./node_modules/@tanstack/react-query/codemods/v4/replace-import-specifier.js

.ts 또는 .tsx 파일을 대상으로 실행하려면 아래 명령을 사용하세요:

npx jscodeshift ./path/to/src/ \
--extensions=ts,tsx \
--parser=tsx \
--transform=./node_modules/@tanstack/react-query/codemods/v4/replace-import-specifier.js

TypeScript의 경우 tsx을 파서로 사용해야 합니다. 그렇지 않으면 codemod가 제대로 적용되지 않는다는 점에 유의하세요!

참고: codemod를 적용하면 코드 서식이 깨질 수 있으므로, codemod를 적용한 후 prettier 및/또는 eslint를 실행하는 것을 잊지 마세요!

참고: codemod는 가져오기만 변경합니다. 별도의 devtools 패키지는 여전히 수동으로 설치해야 합니다.

쿼리 키(및 뮤테이션 키)는 Array여야 합니다

v3에서 Query 및 Mutation Key는 String 또는 Array일 수 있었습니다. 내부적으로 React Query는 항상 Array Key만 사용해 왔으며, 이를 사용자에게 노출한 경우도 있었습니다. 예를 들어 queryFn에서는 기본 Query 함수를 더 쉽게 다룰 수 있도록 항상 키를 Array로 받았습니다.

그러나 이 개념을 모든 API에 일관되게 적용하지는 않았습니다. 예를 들어 Query Filters에서 predicate 함수를 사용하면 원시 Query Key를 얻게 됩니다. 이로 인해 Array와 String이 혼합된 Query Key를 사용하는 경우 이러한 함수로 작업하기가 어렵습니다. 전역 callback을 사용할 때도 마찬가지였습니다.

모든 API를 간소화하기 위해 모든 키를 Array로만 만들기로 결정했습니다:

;-useQuery('todos', fetchTodos) + // [!code --]
useQuery(['todos'], fetchTodos) // [!code ++]

Codemod

이 마이그레이션을 더 쉽게 하기 위해 codemod를 제공하기로 결정했습니다.

codemod는 호환성을 깨뜨리는 변경 사항의 마이그레이션을 돕기 위해 최선을 다합니다. 생성된 코드를 철저히 검토해 주세요! 또한 codemod로 찾을 수 없는 예외 사례도 있으므로 로그 출력을 주의 깊게 살펴보세요.

다음 명령 중 하나(또는 둘 다)를 사용하여 쉽게 적용할 수 있습니다:

.js 또는 .jsx 파일을 대상으로 실행하려면 아래 명령을 사용하세요:

npx jscodeshift ./path/to/src/ \
--extensions=js,jsx \
--transform=./node_modules/@tanstack/react-query/codemods/v4/key-transformation.js

.ts 또는 .tsx 파일을 대상으로 실행하려면 아래 명령을 사용하세요:

npx jscodeshift ./path/to/src/ \
--extensions=ts,tsx \
--parser=tsx \
--transform=./node_modules/@tanstack/react-query/codemods/v4/key-transformation.js

TypeScript의 경우 tsx을 파서로 사용해야 합니다. 그렇지 않으면 codemod가 제대로 적용되지 않는다는 점에 유의하세요!

참고: codemod를 적용하면 코드 서식이 깨질 수 있으므로, codemod를 적용한 후 prettier 및/또는 eslint를 실행하는 것을 잊지 마세요!

유휴 상태가 제거되었습니다.

오프라인 지원을 개선하기 위한 새로운 fetchStatus의 도입으로 idle 상태는 더 이상 중요하지 않게 되었습니다. fetchStatus: 'idle'가 동일한 상태를 더 잘 나타내기 때문입니다. 자세한 내용은 서로 다른 두 상태가 존재하는 이유를 읽어보세요.

이는 주로 아직 data가 없는 disabled 쿼리에 영향을 미치는데, 이전에는 해당 쿼리가 idle 상태였기 때문입니다:

- status: 'idle' // [!code --]
+ status: 'loading' // [!code ++]
+ fetchStatus: 'idle' // [!code ++]

또한 종속 쿼리 가이드도 살펴보세요

비활성화된 쿼리

이 변경으로 인해 비활성화된 쿼리(일시적으로 비활성화된 쿼리 포함)는 loading 상태에서 시작합니다. 마이그레이션을 더 쉽게 하고, 특히 로딩 스피너를 표시할 시점을 알 수 있는 유용한 플래그를 사용하려면 isLoading 대신 isInitialLoading을 확인할 수 있습니다:

;-isLoading + // [!code --]
isInitialLoading // [!code ++]

쿼리 비활성화 가이드도 참조하세요

useQueries를 위한 새 API

이제 useQueries 훅은 queries prop이 있는 객체를 입력으로 받습니다. queries prop의 값은 쿼리 배열입니다(이 배열은 v3에서 useQueries에 전달했던 배열과 동일합니다).

;-useQueries([
{ queryKey1, queryFn1, options1 },
{ queryKey2, queryFn2, options2 },
]) + // [!code --]
useQueries({
queries: [
{ queryKey1, queryFn1, options1 },
{ queryKey2, queryFn2, options2 },
],
}) // [!code ++]

Undefined는 성공한 쿼리에서 허용되지 않는 캐시 값입니다

undefined를 반환하여 업데이트를 중단할 수 있도록 하려면 undefined를 허용되지 않는 캐시 값으로 만들어야 했습니다. 이는 react-query의 다른 개념과도 일치합니다. 예를 들어 initialData 함수에서 undefined를 반환해도 데이터가 설정되지 않습니다.

또한 queryFn에 로깅을 추가하여 Promise<void>를 발생시키는 버그를 만들기 쉽습니다:

useQuery(['key'], () =>
axios.get(url).then((result) => console.log(result.data)),
)

이제 이는 타입 수준에서 허용되지 않습니다. 런타임에는 undefined가 _실패한 Promise_로 변환되므로 error가 발생하며, 개발 모드에서는 콘솔에도 기록됩니다.

쿼리와 뮤테이션을 실행하려면 기본적으로 네트워크 연결이 필요합니다

온라인 / 오프라인 지원에 관한 새 기능 발표네트워크 모드 전용 페이지도 읽어 보세요.

React Query는 Promise를 생성하는 모든 대상에 사용할 수 있는 Async State Manager이지만, 대부분 data fetching library와 함께 데이터를 가져오는 데 사용됩니다. 따라서 기본적으로 network connection이 없으면 쿼리와 뮤테이션이 paused됩니다. 이전 동작을 사용하려면 쿼리와 뮤테이션 모두에 대해 networkMode: offlineFirst를 전역으로 설정할 수 있습니다:

new QueryClient({
defaultOptions: {
queries: {
networkMode: 'offlineFirst',
},
mutations: {
networkMode: 'offlineFirst',
},
},
})

notifyOnChangeProps 속성은 더 이상 "tracked"을 값으로 허용하지 않습니다

notifyOnChangeProps 옵션은 더 이상 "tracked" 값을 허용하지 않습니다. 대신 useQuery는 기본적으로 속성을 추적합니다. notifyOnChangeProps: "tracked"를 사용하는 모든 쿼리에서 이 옵션을 제거하도록 업데이트해야 합니다.

쿼리가 변경될 때마다 다시 렌더링하는 v3의 기본 동작을 모방하기 위해 특정 쿼리에서 이를 우회하려는 경우, 이제 notifyOnChangeProps는 기본 스마트 추적 최적화를 사용하지 않도록 설정하는 "all" 값을 허용합니다.

notifyOnChangePropsExclusion이 제거되었습니다

v4에서 notifyOnChangePropsundefined 대신 v3의 "tracked" 동작을 기본값으로 사용합니다. 이제 "tracked"이 v4의 기본 동작이므로 이 설정 옵션을 포함하는 것은 더 이상 의미가 없습니다.

cancelRefetch의 일관된 동작

cancelRefetch 옵션은 쿼리를 명령형으로 가져오는 모든 함수, 즉 다음 함수에 전달할 수 있습니다:

  • queryClient.refetchQueries
  • queryClient.invalidateQueries
  • queryClient.resetQueries
  • useQuery에서 반환된 refetch
  • fetchNextPagefetchPreviousPage(useInfiniteQuery에서 반환됨)

fetchNextPagefetchPreviousPage를 제외하면 이 플래그의 기본값은 false였으며, 이는 일관성이 없고 잠재적으로 문제가 될 수 있었습니다. 이전의 느린 가져오기가 이미 진행 중이면 이 다시 가져오기를 건너뛰었을 것이므로, 뮤테이션 후 refetchQueries 또는 invalidateQueries를 호출해도 최신 결과를 얻지 못할 수 있었습니다.

작성한 코드가 쿼리를 능동적으로 다시 가져오는 경우, 기본적으로 가져오기를 다시 시작해야 한다고 생각합니다.

이것이 이제 이 플래그가 위에서 언급한 모든 메서드에서 기본적으로 _true_인 이유입니다. 또한 refetchQueries를 기다리지 않고 연달아 두 번 호출하면 이제 첫 번째 가져오기를 취소하고 두 번째 호출로 다시 시작한다는 의미이기도 합니다:

queryClient.refetchQueries({ queryKey: ['todos'] })
// this will abort the previous refetch and start a new fetch
queryClient.refetchQueries({ queryKey: ['todos'] })

cancelRefetch:false를 명시적으로 전달하여 이 동작을 사용하지 않도록 선택할 수 있습니다:

queryClient.refetchQueries({ queryKey: ['todos'] })
// this will not abort the previous refetch - it will just be ignored
queryClient.refetchQueries({ queryKey: ['todos'] }, { cancelRefetch: false })

참고: 예를 들어 쿼리가 마운트되거나 창 포커스로 인해 다시 가져오기가 수행되는 등 자동으로 트리거된 가져오기의 동작에는 변경 사항이 없습니다.

쿼리 필터

쿼리 필터는 쿼리와 일치시키기 위한 특정 조건을 갖는 객체입니다. 과거에는 필터 옵션이 대부분 boolean 플래그의 조합이었습니다. 하지만 이러한 플래그를 조합하면 불가능한 상태가 생길 수 있습니다. 구체적으로는 다음과 같습니다:

active?: boolean
- When set to true it will match active queries.
- When set to false it will match inactive queries.
inactive?: boolean
- When set to true it will match inactive queries.
- When set to false it will match active queries.

이 플래그들은 상호 배타적이므로 함께 사용하면 제대로 작동하지 않습니다. 설명으로 판단하면 두 플래그 모두에 false를 설정할 경우 모든 쿼리와 일치하거나 어떤 쿼리와도 일치하지 않을 수 있는데, 이는 별로 타당하지 않습니다.

v4에서는 의도를 더 명확하게 보여주기 위해 이러한 필터를 단일 필터로 결합했습니다:

- active?: boolean // [!code --]
- inactive?: boolean // [!code --]
+ type?: 'active' | 'inactive' | 'all' // [!code ++]

필터의 기본값은 all이며, active 또는 inactive 쿼리만 일치하도록 선택할 수 있습니다.

refetchActive / refetchInactive

queryClient.invalidateQueries에는 유사한 플래그가 두 개 더 있었습니다:

refetchActive: Boolean
- Defaults to true
- When set to false, queries that match the refetch predicate and are actively being rendered
via useQuery and friends will NOT be refetched in the background, and only marked as invalid.
refetchInactive: Boolean
- Defaults to false
- When set to true, queries that match the refetch predicate and are not being rendered
via useQuery and friends will be both marked as invalid and also refetched in the background

같은 이유로 다음 항목도 결합되었습니다:

- refetchActive?: boolean // [!code --]
- refetchInactive?: boolean // [!code --]
+ refetchType?: 'active' | 'inactive' | 'all' | 'none' // [!code ++]

refetchActive의 기본값이 true였으므로 이 플래그의 기본값은 active입니다. 즉, invalidateQueries에 다시 가져오기를 전혀 수행하지 않도록 지시할 방법도 필요하므로 여기서는 네 번째 옵션(none)도 허용합니다.

onSuccess는 더 이상 setQueryData에서 호출되지 않습니다.

이는 많은 사람에게 혼란을 주었으며 onSuccess 내부에서 setQueryData가 호출되면 무한 루프도 발생시켰습니다. 또한 staleTime과 함께 사용할 때 오류가 자주 발생했는데, 캐시에서만 데이터를 읽으면 onSuccess가 호출되지 않았기 때문입니다.

onErroronSettled와 마찬가지로 이제 onSuccess 콜백은 요청이 이루어지는 것과 연결됩니다. 요청 없음 -> 콜백 없음.

data 필드의 변경 사항을 수신하려면 data가 의존성 Array에 포함된 useEffect를 사용하는 것이 가장 좋습니다. React Query가 구조적 공유를 통해 안정적인 데이터를 보장하므로, effect는 백그라운드에서 다시 가져올 때마다 실행되지 않고 데이터 내부의 무언가가 변경된 경우에만 실행됩니다:

const { data } = useQuery({ queryKey, queryFn })
React.useEffect(() => mySideEffectHere(data), [data])

persistQueryClient 및 이에 해당하는 persister 플러그인은 더 이상 실험적 기능이 아니며 이름이 변경되었습니다.

플러그인 createWebStoragePersistorcreateAsyncStoragePersistor의 이름이 각각 createSyncStoragePersistercreateAsyncStoragePersister로 변경되었습니다. persistQueryClient의 인터페이스 Persistor 이름도 Persister로 변경되었습니다. 이 변경의 배경은 이 Stack Exchange 글에서 확인하세요.

이 플러그인들은 더 이상 실험적이지 않으므로 import 경로도 업데이트되었습니다:

- import { persistQueryClient } from 'react-query/persistQueryClient-experimental' // [!code --]
- import { createWebStoragePersistor } from 'react-query/createWebStoragePersistor-experimental' // [!code --]
- import { createAsyncStoragePersistor } from 'react-query/createAsyncStoragePersistor-experimental' // [!code --]

+ import { persistQueryClient } from '@tanstack/react-query-persist-client' // [!code ++]
+ import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister' // [!code ++]
+ import { createAsyncStoragePersister } from '@tanstack/query-async-storage-persister' // [!code ++]

Promise의 cancel 메서드는 더 이상 지원되지 않습니다

Promise에 cancel 함수를 정의할 수 있게 하고 라이브러리에서 쿼리 취소를 지원하는 데 사용되던 기존 cancel 메서드는 제거되었습니다. 내부적으로 AbortController API를 사용하며 쿼리 함수에서 쿼리 취소를 지원할 AbortSignal 인스턴스를 제공하는 최신 API(v3.30.0에서 도입됨)을 쿼리 취소에 사용하는 것을 권장합니다.

TypeScript

이제 타입을 사용하려면 TypeScript v4.1 이상이 필요합니다

지원되는 브라우저

v4부터 React Query는 최신 브라우저에 최적화되어 있습니다. 더 현대적이고 성능이 우수하며 작은 번들을 생성하도록 browserslist를 업데이트했습니다. 요구 사항은 여기에서 확인할 수 있습니다.

setLogger가 제거됩니다

setLogger를 호출하여 logger를 전역적으로 변경할 수 있었습니다. v4에서는 해당 함수가 QueryClient를 생성할 때 지정하는 선택적 필드로 대체됩니다.

- import { QueryClient, setLogger } from 'react-query'; // [!code --]
+ import { QueryClient } from '@tanstack/react-query'; // [!code ++]

- setLogger(customLogger) // [!code --]
- const queryClient = new QueryClient(); // [!code --]
+ const queryClient = new QueryClient({ logger: customLogger }) // [!code ++]

서버 측에는 기본 수동 가비지 컬렉션이 없습니다 {#no-default-manual-garbage-collection-server-side}

v3에서 React Query는 기본적으로 쿼리 결과를 5분 동안 캐시한 다음 해당 데이터를 수동으로 가비지 컬렉션했습니다. 이 기본값은 서버 측 React Query에도 적용되었습니다.

이로 인해 메모리 사용량이 많아지고 이 수동 가비지 컬렉션이 완료되기를 기다리는 프로세스가 중단되었습니다. v4에서는 이제 기본적으로 서버 측 cacheTimeInfinity로 설정되어 수동 가비지 컬렉션을 사실상 비활성화합니다(요청이 완료되면 NodeJS 프로세스가 모든 항목을 정리합니다).

이 변경 사항은 Next.js를 사용하는 경우처럼 서버 측 React Query 사용자에게만 영향을 줍니다. cacheTime을 수동으로 설정하는 경우에는 영향을 받지 않습니다(동일한 동작을 구현하고 싶을 수는 있습니다).

프로덕션 환경에서 로깅

v4부터 react-query는 많은 사용자에게 혼란을 주었던 오류(예: 가져오기 실패)를 프로덕션 모드에서 더 이상 콘솔에 기록하지 않습니다. 개발 모드에서는 오류가 계속 표시됩니다.

ESM 지원

이제 React Query는 package.json "exports"를 지원하며 CommonJS와 ESM 모두에 대한 Node의 네이티브 해석과 완전히 호환됩니다. 대부분의 사용자에게 이 변경이 호환성을 깨뜨릴 것으로 예상하지는 않지만, 프로젝트로 가져올 수 있는 파일은 공식적으로 지원하는 진입점으로만 제한됩니다.

간소화된 NotifyEvents

QueryCache를 수동으로 구독하면 항상 QueryCacheNotifyEvent가 제공되었지만, MutationCache의 경우에는 그렇지 않았습니다. 동작을 간소화하고 이에 맞게 이벤트 이름도 조정했습니다.

QueryCacheNotifyEvent

- type: 'queryAdded' // [!code --]
+ type: 'added' // [!code ++]
- type: 'queryRemoved' // [!code --]
+ type: 'removed' // [!code ++]
- type: 'queryUpdated' // [!code --]
+ type: 'updated' // [!code ++]

MutationCacheNotifyEvent

MutationCacheNotifyEventQueryCacheNotifyEvent와 동일한 타입을 사용합니다.

참고: 이는 queryCache.subscribe 또는 mutationCache.subscribe를 통해 캐시를 수동으로 구독하는 경우에만 관련이 있습니다

별도의 하이드레이션 내보내기가 제거되었습니다

버전 3.22.0부터 하이드레이션 유틸리티가 React Query 코어로 이동했습니다. v3에서는 react-query/hydration의 기존 export를 계속 사용할 수 있었지만, 이 export들은 v4에서 제거되었습니다.

- import { dehydrate, hydrate, useHydrate, Hydrate } from 'react-query/hydration' // [!code --]
+ import { dehydrate, hydrate, useHydrate, Hydrate } from '@tanstack/react-query' // [!code ++]

queryClient, querymutation에서 문서화되지 않은 메서드를 제거했습니다

QueryClientcancelMutationsexecuteMutation 메서드는 문서화되지 않았고 내부에서도 사용되지 않았으므로 제거했습니다. 이는 mutationCache에서 제공되는 메서드를 감싼 래퍼에 불과했으므로, 여전히 executeMutation의 기능을 사용할 수 있습니다.

- executeMutation< // [!code --]
- TData = unknown, // [!code --]
- TError = unknown, // [!code --]
- TVariables = void, // [!code --]
- TContext = unknown // [!code --]
- >( // [!code --]
- options: MutationOptions<TData, TError, TVariables, TContext> // [!code --]
- ): Promise<TData> { // [!code --]
- return this.mutationCache.build(this, options).execute() // [!code --]
- } // [!code --]

또한 query.setDefaultOptions도 사용되지 않았기 때문에 제거되었습니다. mutation.cancel은 실제로 전송 중인 요청을 취소하지 않았기 때문에 제거되었습니다.

src/react 디렉터리의 이름이 src/reactjs로 변경되었습니다

이전에는 React Query에 react 모듈에서 가져오는 react이라는 디렉터리가 있었습니다. 이로 인해 일부 Jest 구성에서 문제가 발생하여 다음과 같은 테스트를 실행할 때 오류가 발생할 수 있었습니다:

TypeError: Cannot read property 'createContext' of undefined

디렉터리 이름을 변경했으므로 더 이상 문제가 되지 않습니다.

프로젝트에서 'react-query'만이 아니라 'react-query/react'에서 직접 가져온 항목이 있다면 import를 업데이트해야 합니다:

- import { QueryClientProvider } from 'react-query/react'; // [!code --]
+ import { QueryClientProvider } from '@tanstack/react-query/reactjs'; // [!code ++]

새로운 기능 🚀

v4에는 훌륭한 새 기능들이 포함되어 있습니다:

React 18 지원

React 18은 올해 초에 출시되었으며, 이제 v4는 이를 비롯해 여기에 포함된 새로운 동시성 기능을 일급으로 지원합니다.

올바른 오프라인 지원

v3에서 React Query는 항상 쿼리와 뮤테이션을 실행해 왔지만, 이를 재시도하려면 인터넷에 연결되어 있어야 한다고 가정했습니다. 이로 인해 여러 혼란스러운 상황이 발생했습니다:

  • 오프라인 상태에서 쿼리를 마운트하면 로딩 상태로 전환되고 요청이 실패하며, 실제로 가져오는 중이 아니더라도 다시 온라인 상태가 될 때까지 로딩 상태를 유지합니다.
  • 마찬가지로 오프라인 상태에서 재시도를 꺼 두면 쿼리가 실행되었다가 실패하고 오류 상태로 전환됩니다.
  • 오프라인 상태에서 네트워크 연결이 반드시 필요하지 않은 쿼리를 실행하려고 하지만(데이터를 가져오는 것 외의 용도로도 React Query를 사용할 수 있기 때문입니다), 다른 이유로 실패할 수 있습니다. 그러면 해당 쿼리는 다시 온라인 상태가 될 때까지 일시 중지됩니다.
  • 오프라인 상태에서는 창 포커스 시 다시 가져오기가 전혀 아무 작업도 하지 않았습니다.

v4에서 React Query는 이러한 모든 문제를 해결하기 위해 새로운 networkMode를 도입합니다. 자세한 내용은 새로운 네트워크 모드 전용 페이지를 읽어보세요.

기본적으로 추적되는 쿼리

React Query는 기본적으로 쿼리 속성을 "추적"하며, 이를 통해 렌더링 최적화가 상당히 향상됩니다. 이 기능은 v3.6.0부터 존재했으며 이제 v4에서 기본 동작이 되었습니다.

setQueryData를 사용하여 업데이트 중단하기

이제 setQueryData의 함수형 업데이터 형식을 사용할 때 undefined를 반환하여 업데이트를 중단할 수 있습니다. 이는 undefinedpreviousValue로 제공되는 경우, 즉 현재 캐시된 항목이 없고 todo를 토글하는 예시처럼 항목을 생성하고 싶지 않거나 생성할 수 없는 경우에 유용합니다:

queryClient.setQueryData(['todo', id], (previousTodo) =>
previousTodo ? { ...previousTodo, done: true } : undefined,
)

Mutation 캐시 가비지 컬렉션

이제 뮤테이션도 쿼리와 마찬가지로 자동으로 가비지 컬렉션될 수 있습니다. 뮤테이션의 기본 cacheTime도 5분으로 설정됩니다.

여러 Provider를 위한 사용자 지정 컨텍스트

이제 훅을 해당 훅과 일치하는 Provider와 연결하도록 사용자 지정 context를 지정할 수 있습니다. 컴포넌트 트리에 여러 React Query Provider 인스턴스가 있을 수 있으며 훅이 올바른 Provider 인스턴스를 사용하도록 보장해야 할 때 이는 매우 중요합니다.

예시:

  1. 데이터 패키지를 생성합니다.
// Our first data package: @my-scope/container-data

const context = React.createContext<QueryClient | undefined>(undefined)
const queryClient = new QueryClient()

export const useUser = () => {
return useQuery(USER_KEY, USER_FETCHER, {
context,
})
}

export const ContainerDataProvider = ({
children,
}: {
children: React.ReactNode
}) => {
return (
<QueryClientProvider client={queryClient} context={context}>
{children}
</QueryClientProvider>
)
}
  1. 두 번째 데이터 패키지를 생성합니다.
// Our second data package: @my-scope/my-component-data

const context = React.createContext<QueryClient | undefined>(undefined)
const queryClient = new QueryClient()

export const useItems = () => {
return useQuery(ITEMS_KEY, ITEMS_FETCHER, {
context,
})
}

export const MyComponentDataProvider = ({
children,
}: {
children: React.ReactNode
}) => {
return (
<QueryClientProvider client={queryClient} context={context}>
{children}
</QueryClientProvider>
)
}
  1. 애플리케이션에서 이 두 데이터 패키지를 사용합니다.
// Our application

import { ContainerDataProvider, useUser } from "@my-scope/container-data";
import { AppDataProvider } from "@my-scope/app-data";
import { MyComponentDataProvider, useItems } from "@my-scope/my-component-data";

<ContainerDataProvider> // <-- Provides container data (like "user") using its own React Query provider
...
<AppDataProvider> // <-- Provides app data using its own React Query provider (unused in this example)
...
<MyComponentDataProvider> // <-- Provides component data (like "items") using its own React Query provider
<MyComponent />
</MyComponentDataProvider>
...
</AppDataProvider>
...
</ContainerDataProvider>

// Example of hooks provided by the "DataProvider" components above:
const MyComponent = () => {
const user = useUser() // <-- Uses the context specified in ContainerDataProvider.
const items = useItems() // <-- Uses the context specified in MyComponentDataProvider
...
}