react query: globally show error toast

This commit is contained in:
vchikalkin 2025-02-13 18:26:22 +03:00
parent 284142eb4f
commit bbf3a98bd5
6 changed files with 88 additions and 10 deletions

View File

@ -1,4 +1,5 @@
import { AuthProvider } from '@/providers/auth';
import { ErrorProvider } from '@/providers/error';
import { QueryProvider } from '@/providers/query';
import { ThemeProvider } from '@/providers/theme-provider';
import { I18nProvider } from '@/utils/i18n/provider';
@ -17,13 +18,15 @@ export default async function RootLayout({ children }: Readonly<PropsWithChildre
return (
<html lang={locale}>
<body className="flex min-h-screen flex-col bg-app-background">
<I18nProvider>
<ThemeProvider>
<AuthProvider>
<QueryProvider>{children}</QueryProvider>
</AuthProvider>
</ThemeProvider>
</I18nProvider>
<ErrorProvider>
<I18nProvider>
<ThemeProvider>
<AuthProvider>
<QueryProvider>{children}</QueryProvider>
</AuthProvider>
</ThemeProvider>
</I18nProvider>
</ErrorProvider>
</body>
</html>
);

View File

@ -0,0 +1,11 @@
import { Toaster } from '@repo/ui/components/ui/sonner';
import { type PropsWithChildren } from 'react';
export function ErrorProvider({ children }: Readonly<PropsWithChildren>) {
return (
<>
<Toaster position="top-center" richColors />
{children}
</>
);
}

View File

@ -1,7 +1,14 @@
'use client';
import { toast } from '@repo/ui/components/ui/sonner';
// Since QueryClientProvider relies on useContext under the hood, we have to put 'use client' on top
import { isServer, QueryClient, QueryClientProvider } from '@tanstack/react-query';
import {
isServer,
MutationCache,
QueryCache,
QueryClient,
QueryClientProvider,
} from '@tanstack/react-query';
function makeQueryClient() {
return new QueryClient({
@ -12,6 +19,12 @@ function makeQueryClient() {
staleTime: 60 * 1_000,
},
},
mutationCache: new MutationCache({
onError: () => toast.error('Ошибка при отправке данных'),
}),
queryCache: new QueryCache({
onError: () => toast.error('Ошибка при загрузке данных'),
}),
});
}

View File

@ -51,8 +51,10 @@
"@radix-ui/react-scroll-area": "^1.2.2",
"@radix-ui/react-select": "^2.1.4",
"date-fns": "^4.1.0",
"next-themes": "^0.4.4",
"react": "catalog:",
"react-day-picker": "8.10.1",
"react-dom": "catalog:"
"react-dom": "catalog:",
"sonner": "^1.7.4"
}
}

View File

@ -0,0 +1,31 @@
'use client';
import { useTheme } from 'next-themes';
import { Toaster as Sonner } from 'sonner';
type ToasterProps = React.ComponentProps<typeof Sonner>;
function Toaster({ ...props }: ToasterProps) {
const { theme = 'system' } = useTheme();
return (
<Sonner
className="toaster group"
theme={theme as ToasterProps['theme']}
toastOptions={{
classNames: {
actionButton: 'group-[.toast]:bg-primary group-[.toast]:text-primary-foreground',
cancelButton: 'group-[.toast]:bg-muted group-[.toast]:text-muted-foreground',
description: 'group-[.toast]:text-muted-foreground',
toast:
'group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg',
},
}}
{...props}
/>
);
}
export { Toaster };
export { toast } from 'sonner';

20
pnpm-lock.yaml generated
View File

@ -330,6 +330,9 @@ importers:
date-fns:
specifier: ^4.1.0
version: 4.1.0
next-themes:
specifier: ^0.4.4
version: 0.4.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
react:
specifier: 'catalog:'
version: 19.0.0
@ -339,6 +342,9 @@ importers:
react-dom:
specifier: 'catalog:'
version: 19.0.0(react@19.0.0)
sonner:
specifier: ^1.7.4
version: 1.7.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
devDependencies:
'@radix-ui/react-slot':
specifier: ^1.1.1
@ -4615,6 +4621,7 @@ packages:
lodash.get@4.4.2:
resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==}
deprecated: This package is deprecated. Use the optional chaining (?.) operator instead.
lodash.includes@4.3.0:
resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==}
@ -5660,6 +5667,12 @@ packages:
snake-case@3.0.4:
resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==}
sonner@1.7.4:
resolution: {integrity: sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw==}
peerDependencies:
react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
@ -11703,7 +11716,7 @@ snapshots:
normalize-package-data@2.5.0:
dependencies:
hosted-git-info: 2.8.9
resolve: 1.22.8
resolve: 1.22.10
semver: 5.7.2
validate-npm-package-license: 3.0.4
@ -12482,6 +12495,11 @@ snapshots:
dot-case: 3.0.4
tslib: 2.8.1
sonner@1.7.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
dependencies:
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
source-map-js@1.2.1: {}
source-map@0.6.1: {}