F: Implement ErrorFallback for root

This commit is contained in:
Ivan 2025-01-30 12:55:51 +03:00
parent 532bf24df6
commit d139c07b7a
3 changed files with 40 additions and 48 deletions

View File

@ -1,5 +1,4 @@
import { Suspense } from 'react'; import { Suspense } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import { Outlet } from 'react-router'; import { Outlet } from 'react-router';
import ConceptToaster from '@/app/ConceptToaster'; import ConceptToaster from '@/app/ConceptToaster';
@ -10,22 +9,10 @@ import ModalLoader from '@/components/ui/ModalLoader';
import { useAppLayoutStore, useMainHeight, useViewportHeight } from '@/stores/appLayout'; import { useAppLayoutStore, useMainHeight, useViewportHeight } from '@/stores/appLayout';
import { globals } from '@/utils/constants'; import { globals } from '@/utils/constants';
import ErrorFallback from './ErrorFallback';
import { GlobalDialogs } from './GlobalDialogs'; import { GlobalDialogs } from './GlobalDialogs';
import { GlobalTooltips } from './GlobalTooltips'; import { GlobalTooltips } from './GlobalTooltips';
import { NavigationState } from './Navigation/NavigationContext'; import { NavigationState } from './Navigation/NavigationContext';
const resetState = () => {
console.log('Resetting state after error fallback');
};
const logError = (error: Error, info: { componentStack?: string | null | undefined }) => {
console.log('Error fallback: ' + error.message);
if (info.componentStack) {
console.log('Component stack: ' + info.componentStack);
}
};
function ApplicationLayout() { function ApplicationLayout() {
const mainHeight = useMainHeight(); const mainHeight = useMainHeight();
const viewportHeight = useViewportHeight(); const viewportHeight = useViewportHeight();
@ -35,41 +22,39 @@ function ApplicationLayout() {
const noFooter = useAppLayoutStore(state => state.noFooter); const noFooter = useAppLayoutStore(state => state.noFooter);
return ( return (
<ErrorBoundary FallbackComponent={ErrorFallback} onError={logError} onReset={resetState}> <NavigationState>
<NavigationState> <div className='min-w-[20rem] antialiased h-full max-w-[120rem] mx-auto'>
<div className='min-w-[20rem] antialiased h-full max-w-[120rem] mx-auto'> <ConceptToaster
<ConceptToaster className='text-[14px] cc-animate-position'
className='text-[14px] cc-animate-position' style={{ marginTop: noNavigationAnimation ? '1.5rem' : '3.5rem' }}
style={{ marginTop: noNavigationAnimation ? '1.5rem' : '3.5rem' }} autoClose={3000}
autoClose={3000} draggable={false}
draggable={false} pauseOnFocusLoss={false}
pauseOnFocusLoss={false} />
/>
<Suspense fallback={<ModalLoader />}> <Suspense fallback={<ModalLoader />}>
<GlobalDialogs /> <GlobalDialogs />
</Suspense> </Suspense>
<GlobalTooltips /> <GlobalTooltips />
<Navigation /> <Navigation />
<div <div
id={globals.main_scroll} id={globals.main_scroll}
className='overflow-x-auto max-w-[100vw]' className='overflow-x-auto max-w-[100vw]'
style={{ style={{
maxHeight: viewportHeight maxHeight: viewportHeight
}} }}
> >
<main className='cc-scroll-y' style={{ overflowY: showScroll ? 'scroll' : 'auto', minHeight: mainHeight }}> <main className='cc-scroll-y' style={{ overflowY: showScroll ? 'scroll' : 'auto', minHeight: mainHeight }}>
<Suspense fallback={<Loader />}> <Suspense fallback={<Loader />}>
<Outlet /> <Outlet />
</Suspense> </Suspense>
</main> </main>
{!noNavigation && !noFooter ? <Footer /> : null} {!noNavigation && !noFooter ? <Footer /> : null}
</div>
</div> </div>
</NavigationState> </div>
</ErrorBoundary> </NavigationState>
); );
} }

View File

@ -1,13 +1,19 @@
import { type FallbackProps } from 'react-error-boundary'; import { useNavigate, useRouteError } from 'react-router';
import InfoError from '@/components/info/InfoError'; import InfoError from '@/components/info/InfoError';
import Button from '@/components/ui/Button'; import Button from '@/components/ui/Button';
function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) { function ErrorFallback() {
const error = useRouteError();
const router = useNavigate();
function resetErrorBoundary() {
Promise.resolve(router('/')).catch(console.log);
}
return ( return (
<div className='flex flex-col gap-3 my-3 items-center antialiased' role='alert'> <div className='flex flex-col gap-3 my-3 items-center antialiased' role='alert'>
<h1 className='my-2'>Что-то пошло не так!</h1> <h1 className='my-2'>Что-то пошло не так!</h1>
<Button onClick={resetErrorBoundary} text='Попробовать еще раз' /> <Button onClick={resetErrorBoundary} text='Вернуться на главную' />
<InfoError error={error as Error} /> <InfoError error={error as Error} />
</div> </div>
); );

View File

@ -13,13 +13,14 @@ import LoginPage from '@/pages/LoginPage';
import NotFoundPage from '@/pages/NotFoundPage'; import NotFoundPage from '@/pages/NotFoundPage';
import ApplicationLayout from './ApplicationLayout'; import ApplicationLayout from './ApplicationLayout';
import ErrorFallback from './ErrorFallback';
import { routes } from './urls'; import { routes } from './urls';
export const Router = createBrowserRouter([ export const Router = createBrowserRouter([
{ {
path: '/', path: '/',
element: <ApplicationLayout />, element: <ApplicationLayout />,
errorElement: <NotFoundPage />, errorElement: <ErrorFallback />,
loader: prefetchAuth, loader: prefetchAuth,
hydrateFallbackElement: <Loader />, hydrateFallbackElement: <Loader />,
children: [ children: [