@uniai-fe/next-providers 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +27 -0
- package/README.md +3 -0
- package/package.json +81 -0
- package/src/NextRoots.tsx +57 -0
- package/src/index.tsx +3 -0
- package/src/lib/RouterEventsDetector.tsx +30 -0
- package/src/lib/SetQueryCookie.tsx +28 -0
- package/src/lib/StyledComponentsRegistry.tsx +35 -0
- package/src/lib/SystemThemeChecker.tsx +39 -0
- package/src/provider/AirtableProvider.tsx +41 -0
- package/src/provider/JotaiProvider.tsx +23 -0
- package/src/provider/ReactQueryProvider.tsx +82 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 UNIAI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
This project includes third-party software governed by additional licenses,
|
|
26
|
+
including Apache License 2.0. Refer to `THIRD_PARTY_NOTICES.md` for the full
|
|
27
|
+
text of those notices and any required attributions.
|
package/README.md
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@uniai-fe/next-providers",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Next.js State Providers for UNIAI FE Projects",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"private": false,
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"homepage": "https://www.uniai.co.kr/",
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"packageManager": "pnpm@10.22.0",
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=22",
|
|
16
|
+
"pnpm": ">=10"
|
|
17
|
+
},
|
|
18
|
+
"author": {
|
|
19
|
+
"name": "GraffitoRyu",
|
|
20
|
+
"email": "yth4135@naver.com",
|
|
21
|
+
"url": "https://github.com/GraffitoRyu"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"src"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"lint": "eslint . --max-warnings=0",
|
|
28
|
+
"typecheck": "tsc --project tsconfig.build.json --noEmit",
|
|
29
|
+
"build": "pnpm typecheck",
|
|
30
|
+
"dev": "tsc --project tsconfig.build.json --watch --noEmit",
|
|
31
|
+
"module:lint": "pnpm lint",
|
|
32
|
+
"module:typecheck": "pnpm typecheck",
|
|
33
|
+
"module:build": "pnpm build",
|
|
34
|
+
"next-providers:build": "pnpm run build",
|
|
35
|
+
"next-providers:dev": "pnpm run dev"
|
|
36
|
+
},
|
|
37
|
+
"main": "./src/index.tsx",
|
|
38
|
+
"module": "./src/index.tsx",
|
|
39
|
+
"exports": {
|
|
40
|
+
".": "./src/index.tsx"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"next": ">= 15",
|
|
44
|
+
"react": ">= 19",
|
|
45
|
+
"react-dom": ">= 19",
|
|
46
|
+
"@tanstack/react-query": ">= 5",
|
|
47
|
+
"jotai": ">= 2",
|
|
48
|
+
"styled-components": ">= 6",
|
|
49
|
+
"airtable": ">= 0.12"
|
|
50
|
+
},
|
|
51
|
+
"peerDependenciesMeta": {
|
|
52
|
+
"optional": {
|
|
53
|
+
"@tanstack/react-query": true,
|
|
54
|
+
"jotai": true,
|
|
55
|
+
"styled-components": true
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@uniai-fe/eslint-config": "workspace:*",
|
|
60
|
+
"@uniai-fe/tsconfig": "workspace:*",
|
|
61
|
+
"@uniai-fe/next-devkit": "workspace:*",
|
|
62
|
+
"@uniai-fe/util-functions": "workspace:*",
|
|
63
|
+
"@uniai-fe/util-jotai": "workspace:*",
|
|
64
|
+
"@uniai-fe/ui-legacy": "workspace:*",
|
|
65
|
+
"@uniai-fe/i18n": "workspace:*",
|
|
66
|
+
"@tanstack/react-query": "^5.90.8",
|
|
67
|
+
"@types/node": "^24.10.1",
|
|
68
|
+
"@types/react": "^19.2.4",
|
|
69
|
+
"@types/react-dom": "^19.2.3",
|
|
70
|
+
"airtable": "^0.12.2",
|
|
71
|
+
"eslint": "^9.39.1",
|
|
72
|
+
"jotai": "^2.15.1",
|
|
73
|
+
"jotai-tanstack-query": "^0.11.0",
|
|
74
|
+
"next": "^15.5.6",
|
|
75
|
+
"prettier": "^3.6.2",
|
|
76
|
+
"react": "^19.2.0",
|
|
77
|
+
"react-dom": "^19.2.0",
|
|
78
|
+
"styled-components": "^6.1.19",
|
|
79
|
+
"typescript": "~5.9.3"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Suspense } from "react";
|
|
2
|
+
import { Modal } from "@uniai-fe/ui-legacy";
|
|
3
|
+
|
|
4
|
+
import StyledComponentsRegistry from "./lib/StyledComponentsRegistry";
|
|
5
|
+
import AirtableProvider from "./provider/AirtableProvider";
|
|
6
|
+
import { JotaiProvider } from "./provider/JotaiProvider";
|
|
7
|
+
import { ReactQueryProvider } from "./provider/ReactQueryProvider";
|
|
8
|
+
import RouterEventDetector from "./lib/RouterEventsDetector";
|
|
9
|
+
import SetQueryCookie from "./lib/SetQueryCookie";
|
|
10
|
+
import SystemThemeChecker from "./lib/SystemThemeChecker";
|
|
11
|
+
import type { SystemLanguageType } from "@uniai-fe/next-devkit/types";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Next.js Root Layout에 적용할 Provider 및 유틸리티 도구 집합
|
|
15
|
+
* @component
|
|
16
|
+
* @param {object} props
|
|
17
|
+
* @param {SystemLanguageType} [props.locale] 언어설정; "ko", "en"
|
|
18
|
+
* @param {object} [props.systemThemeOptions] 시스템 테마 체크 옵션
|
|
19
|
+
* @param {string[]} [props.systemThemeOptions.exceptPaths] 테마 체크 제외 경로
|
|
20
|
+
* @param {React.ReactNode} props.children
|
|
21
|
+
* @desc
|
|
22
|
+
* - React Query
|
|
23
|
+
* - Jotai
|
|
24
|
+
* - Apollo GraphQL
|
|
25
|
+
* - Airtable
|
|
26
|
+
* - Modal / Draggable Modal
|
|
27
|
+
* - System Theme Checker
|
|
28
|
+
* - Router Event Detector
|
|
29
|
+
*/
|
|
30
|
+
export default function NextRoots({
|
|
31
|
+
locale = "ko",
|
|
32
|
+
systemThemeOptions,
|
|
33
|
+
children,
|
|
34
|
+
}: {
|
|
35
|
+
locale?: SystemLanguageType;
|
|
36
|
+
systemThemeOptions?: { exceptPaths: string[] };
|
|
37
|
+
children: React.ReactNode;
|
|
38
|
+
}) {
|
|
39
|
+
return (
|
|
40
|
+
<ReactQueryProvider>
|
|
41
|
+
<JotaiProvider>
|
|
42
|
+
<SetQueryCookie />
|
|
43
|
+
<StyledComponentsRegistry>
|
|
44
|
+
<AirtableProvider locale={locale}>
|
|
45
|
+
{children}
|
|
46
|
+
<Modal.Basic.Provider />
|
|
47
|
+
<Modal.Draggable.Provider />
|
|
48
|
+
</AirtableProvider>
|
|
49
|
+
<SystemThemeChecker exceptPaths={systemThemeOptions?.exceptPaths} />
|
|
50
|
+
<Suspense fallback={null}>
|
|
51
|
+
<RouterEventDetector />
|
|
52
|
+
</Suspense>
|
|
53
|
+
</StyledComponentsRegistry>
|
|
54
|
+
</JotaiProvider>
|
|
55
|
+
</ReactQueryProvider>
|
|
56
|
+
);
|
|
57
|
+
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { usePathname } from "next/navigation";
|
|
4
|
+
import { useEffect } from "react";
|
|
5
|
+
import { useSetAtom } from "jotai";
|
|
6
|
+
|
|
7
|
+
import { modalState, modalDraggableState } from "@uniai-fe/ui-legacy";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 경로변경 이벤트 감지
|
|
11
|
+
* @component
|
|
12
|
+
* @desc
|
|
13
|
+
* - 최상위 layout에서, 경로 변경을 감지
|
|
14
|
+
*/
|
|
15
|
+
export default function RouterEventDetector() {
|
|
16
|
+
const resetModalStack = useSetAtom(modalState);
|
|
17
|
+
const resetDraggableModalStack = useSetAtom(modalDraggableState);
|
|
18
|
+
|
|
19
|
+
const pathname = usePathname();
|
|
20
|
+
|
|
21
|
+
// 경로 변경 감지
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
// 모달 스택 초기화
|
|
24
|
+
resetModalStack([]);
|
|
25
|
+
// 드래거블 모달 스택 초기화
|
|
26
|
+
resetDraggableModalStack([]);
|
|
27
|
+
}, [pathname, resetDraggableModalStack, resetModalStack]);
|
|
28
|
+
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect } from "react";
|
|
4
|
+
import { setServerState } from "@uniai-fe/util-jotai";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 쿠키 요청값 업데이트
|
|
8
|
+
* @component
|
|
9
|
+
* @desc
|
|
10
|
+
* - 클라이언트에서 정보를 받아 서버 쿠키로 요청이 필요한 경우 사용
|
|
11
|
+
*/
|
|
12
|
+
export default function SetQueryCookie() {
|
|
13
|
+
/**
|
|
14
|
+
* 언어설정 쿠키 요청값 업데이트
|
|
15
|
+
*/
|
|
16
|
+
const setLocaleCookie = useCallback(async () => {
|
|
17
|
+
if (typeof window === "undefined") return;
|
|
18
|
+
const locale = localStorage.getItem("systemLocale");
|
|
19
|
+
await setServerState("systemLocale", locale, "ko");
|
|
20
|
+
}, []);
|
|
21
|
+
|
|
22
|
+
// 클라이언트에서 실행
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
setLocaleCookie();
|
|
25
|
+
}, [setLocaleCookie]);
|
|
26
|
+
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { useServerInsertedHTML } from "next/navigation";
|
|
5
|
+
import { ServerStyleSheet, StyleSheetManager } from "styled-components";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* styled-components의 SSR 활용을 위한 레지스트리 구성요소
|
|
9
|
+
* @component
|
|
10
|
+
* @desc
|
|
11
|
+
* - 최상위 layout에서 등록
|
|
12
|
+
*/
|
|
13
|
+
export default function StyledComponentsRegistry({
|
|
14
|
+
children,
|
|
15
|
+
}: {
|
|
16
|
+
children: React.ReactNode;
|
|
17
|
+
}) {
|
|
18
|
+
// Only create stylesheet once with lazy initial state
|
|
19
|
+
// x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
|
|
20
|
+
const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet());
|
|
21
|
+
|
|
22
|
+
useServerInsertedHTML(() => {
|
|
23
|
+
const styles = styledComponentsStyleSheet.getStyleElement();
|
|
24
|
+
styledComponentsStyleSheet.instance.clearTag();
|
|
25
|
+
return <>{styles}</>;
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (typeof window !== "undefined") return <>{children}</>;
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<StyleSheetManager sheet={styledComponentsStyleSheet.instance}>
|
|
32
|
+
{children}
|
|
33
|
+
</StyleSheetManager>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { usePathname } from "next/navigation";
|
|
4
|
+
import { useEffect } from "react";
|
|
5
|
+
import { useAtomValue } from "jotai";
|
|
6
|
+
import { systemThemeState } from "@uniai-fe/util-jotai";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 서비스 테마관리 공급자
|
|
10
|
+
* @component
|
|
11
|
+
* @desc
|
|
12
|
+
* - 최상위 layout에서 관리
|
|
13
|
+
*/
|
|
14
|
+
export default function SystemThemeChecker({
|
|
15
|
+
serviceType = "farm",
|
|
16
|
+
exceptPaths = [],
|
|
17
|
+
}: {
|
|
18
|
+
serviceType?: string;
|
|
19
|
+
exceptPaths?: string[];
|
|
20
|
+
}) {
|
|
21
|
+
const systemTheme = useAtomValue(systemThemeState);
|
|
22
|
+
const pathname = usePathname();
|
|
23
|
+
|
|
24
|
+
// const locale = useLocale();
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (!systemTheme[serviceType] || exceptPaths.includes(pathname)) {
|
|
28
|
+
document.documentElement.classList.remove(`dark-mode`);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
document.documentElement.classList.remove(`dark-mode`);
|
|
32
|
+
if (systemTheme[serviceType] === "dark")
|
|
33
|
+
document.documentElement.classList.add(
|
|
34
|
+
`${systemTheme[serviceType]}-mode`,
|
|
35
|
+
);
|
|
36
|
+
}, [exceptPaths, pathname, serviceType, systemTheme]);
|
|
37
|
+
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import {
|
|
3
|
+
dehydrate,
|
|
4
|
+
HydrationBoundary,
|
|
5
|
+
QueryClient,
|
|
6
|
+
} from "@tanstack/react-query";
|
|
7
|
+
import { getAirtableData } from "@uniai-fe/i18n/api";
|
|
8
|
+
import type { SystemLanguageType } from "@uniai-fe/next-devkit/types";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 다국어 지원을 위한 Airtable 데이터 호출 공급자
|
|
12
|
+
* @component
|
|
13
|
+
* @property {SystemLanguageType} locale "ko", "en", "jp", string
|
|
14
|
+
* @property {React.ReactNode} children
|
|
15
|
+
* @see https://tanstack.com/query/latest/docs/framework/react/guides/advanced-ssr#prefetching-and-dehydrating-data
|
|
16
|
+
* @see https://tanstack.com/query/latest/docs/framework/react/guides/advanced-ssr#nesting-server-components
|
|
17
|
+
* @desc
|
|
18
|
+
* - 데이터를 서버에서 미리 불러오기 위해, 서버 컴포넌트로 구성
|
|
19
|
+
*/
|
|
20
|
+
export default async function AirtableProvider({
|
|
21
|
+
locale: serverLocale,
|
|
22
|
+
children,
|
|
23
|
+
}: {
|
|
24
|
+
locale: SystemLanguageType;
|
|
25
|
+
children: React.ReactNode;
|
|
26
|
+
}) {
|
|
27
|
+
// 서버는 항상 새로운 client를 선언해야 함
|
|
28
|
+
const serverQueryClient = new QueryClient();
|
|
29
|
+
|
|
30
|
+
// 데이터 호출
|
|
31
|
+
await serverQueryClient.prefetchQuery({
|
|
32
|
+
queryKey: ["airtable", serverLocale],
|
|
33
|
+
queryFn: () => getAirtableData(serverLocale),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<HydrationBoundary state={dehydrate(serverQueryClient)}>
|
|
38
|
+
{children}
|
|
39
|
+
</HydrationBoundary>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Provider } from "jotai";
|
|
4
|
+
import { useHydrateAtoms } from "jotai/react/utils";
|
|
5
|
+
import { queryClientAtom } from "jotai-tanstack-query";
|
|
6
|
+
import { getQueryClient } from "./ReactQueryProvider";
|
|
7
|
+
|
|
8
|
+
export function JotaiHydrateAtoms({ children }: { children: React.ReactNode }) {
|
|
9
|
+
useHydrateAtoms([[queryClientAtom, getQueryClient()]]);
|
|
10
|
+
return children;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Jotai 상태 공급자
|
|
15
|
+
* @component
|
|
16
|
+
*/
|
|
17
|
+
export function JotaiProvider({ children }: { children: React.ReactNode }) {
|
|
18
|
+
return (
|
|
19
|
+
<Provider>
|
|
20
|
+
<JotaiHydrateAtoms>{children}</JotaiHydrateAtoms>
|
|
21
|
+
</Provider>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
defaultShouldDehydrateQuery,
|
|
5
|
+
isServer,
|
|
6
|
+
QueryClient,
|
|
7
|
+
QueryClientProvider,
|
|
8
|
+
} from "@tanstack/react-query";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 클라이언트 단에서의 React Query client
|
|
12
|
+
* @type {QueryClient | undefined}
|
|
13
|
+
*/
|
|
14
|
+
let browserQueryClient: QueryClient | undefined = undefined;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* React Query client 생성
|
|
18
|
+
* - 서비스 전역에 적용할 쿼리옵션을 사전에 지정하여 생성
|
|
19
|
+
* @return {QueryClient}
|
|
20
|
+
*/
|
|
21
|
+
function makeQueryClient(): QueryClient {
|
|
22
|
+
return new QueryClient({
|
|
23
|
+
// react-query 전역 설정
|
|
24
|
+
defaultOptions: {
|
|
25
|
+
queries: {
|
|
26
|
+
// onError: queryErrorHandler,
|
|
27
|
+
staleTime: 600000,
|
|
28
|
+
gcTime: 900000, // 기존 cacheTime에서 명칭 변경, 원래 "가비지 콜렉션 시간"을 의미하였음.
|
|
29
|
+
refetchOnMount: false,
|
|
30
|
+
refetchOnReconnect: false,
|
|
31
|
+
refetchOnWindowFocus: false,
|
|
32
|
+
retry: false,
|
|
33
|
+
},
|
|
34
|
+
dehydrate: {
|
|
35
|
+
// include pending queries in dehydration
|
|
36
|
+
shouldDehydrateQuery: query =>
|
|
37
|
+
defaultShouldDehydrateQuery(query) ||
|
|
38
|
+
query.state.status === "pending",
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* React Query client 생성
|
|
46
|
+
* @return {QueryClient}
|
|
47
|
+
*/
|
|
48
|
+
export function getQueryClient(): QueryClient {
|
|
49
|
+
if (isServer) {
|
|
50
|
+
// Server: always make a new query client
|
|
51
|
+
return makeQueryClient();
|
|
52
|
+
} else {
|
|
53
|
+
// Browser: make a new query client if we don't already have one
|
|
54
|
+
// This is very important, so we don't re-make a new client if React
|
|
55
|
+
// suspends during the initial render. This may not be needed if we
|
|
56
|
+
// have a suspense boundary BELOW the creation of the query client
|
|
57
|
+
if (!browserQueryClient) browserQueryClient = makeQueryClient();
|
|
58
|
+
return browserQueryClient;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* ReactQuery 상태 공급자
|
|
64
|
+
* @component
|
|
65
|
+
* @see https://tanstack.com/query/latest/docs/framework/react/guides/advanced-ssr#prefetching-and-dehydrating-data
|
|
66
|
+
* @see https://tanstack.com/query/latest/docs/framework/react/guides/advanced-ssr#nesting-server-components
|
|
67
|
+
*/
|
|
68
|
+
export function ReactQueryProvider({
|
|
69
|
+
children,
|
|
70
|
+
}: {
|
|
71
|
+
children: React.ReactNode;
|
|
72
|
+
}) {
|
|
73
|
+
// NOTE: Avoid useState when initializing the query client if you don't
|
|
74
|
+
// have a suspense boundary between this and the code that may
|
|
75
|
+
// suspend because React will throw away the client on the initial
|
|
76
|
+
// render if it suspends and there is no boundary
|
|
77
|
+
const queryClient = getQueryClient();
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
81
|
+
);
|
|
82
|
+
}
|