Lazy load app routes

This commit is contained in:
schroda
2024-04-29 16:35:08 +02:00
parent 5ab921aba7
commit ba3aefc854
5 changed files with 190 additions and 69 deletions

View File

@@ -31,6 +31,7 @@
"@emotion/react": "11.11.4",
"@emotion/styled": "11.11.5",
"@fontsource/roboto": "5.0.13",
"@loadable/component": "^5.16.4",
"@mui/icons-material": "5.15.15",
"@mui/material": "5.15.15",
"@mui/x-date-pickers": "7.3.1",
@@ -52,6 +53,7 @@
"react-device-detect": "2.2.3",
"react-dom": "18.3.0",
"react-i18next": "14.1.1",
"react-lazily": "^0.9.2",
"react-router-dom": "6.23.0",
"react-virtuoso": "4.7.10",
"stylis": "4.3.2",

View File

@@ -11,35 +11,39 @@ import CssBaseline from '@mui/material/CssBaseline';
import React, { useLayoutEffect } from 'react';
import { Navigate, Route, Routes, useLocation } from 'react-router-dom';
import { loadErrorMessages, loadDevMessages } from '@apollo/client/dev';
import { loadable } from 'react-lazily/loadable';
import { AppContext } from '@/components/context/AppContext';
import { Browse } from '@/screens/Browse';
import { DownloadQueue } from '@/screens/DownloadQueue';
import { Library } from '@/screens/Library';
import { Manga } from '@/screens/Manga';
import { Reader } from '@/screens/Reader';
import { SearchAll } from '@/screens/SearchAll';
import { Settings } from '@/screens/Settings';
import { About } from '@/screens/settings/About';
import { Backup } from '@/screens/settings/Backup';
import { Categories } from '@/screens/settings/Categories';
import { DefaultReaderSettings } from '@/screens/settings/DefaultReaderSettings';
import { SourceConfigure } from '@/screens/SourceConfigure';
import { SourceMangas } from '@/screens/SourceMangas';
import { Updates } from '@/screens/Updates';
import '@/i18n';
import { LibrarySettings } from '@/screens/settings/LibrarySettings';
import { DefaultNavBar } from '@/components/navbar/DefaultNavBar';
import { DownloadSettings } from '@/screens/settings/DownloadSettings.tsx';
import { ServerSettings } from '@/screens/settings/ServerSettings.tsx';
import { ServerUpdateChecker } from '@/components/util/ServerUpdateChecker.tsx';
import { requestManager } from '@/lib/requests/RequestManager.ts';
import { BrowseSettings } from '@/screens/settings/BrowseSettings.tsx';
import { WebUISettings } from '@/screens/settings/WebUISettings.tsx';
import { Migrate } from '@/screens/Migrate.tsx';
import { DeviceSetting } from '@/components/settings/DeviceSetting.tsx';
import { TrackingSettings } from '@/screens/settings/TrackingSettings.tsx';
import { TrackerOAuthLogin } from '@/screens/TrackerOAuthLogin.tsx';
import { WebUIUpdateChecker } from '@/components/util/WebUIUpdateChecker.tsx';
import { ServerUpdateChecker } from '@/components/util/ServerUpdateChecker.tsx';
import { lazyLoadFallback } from '@/util/LazyLoad.tsx';
import { ErrorBoundary } from '@/util/ErrorBoundary.tsx';
const { Browse } = loadable(() => import('@/screens/Browse'), lazyLoadFallback);
const { DownloadQueue } = loadable(() => import('@/screens/DownloadQueue'), lazyLoadFallback);
const { Library } = loadable(() => import('@/screens/Library'), lazyLoadFallback);
const { Manga } = loadable(() => import('@/screens/Manga'), lazyLoadFallback);
const { Reader } = loadable(() => import('@/screens/Reader'), lazyLoadFallback);
const { SearchAll } = loadable(() => import('@/screens/SearchAll'), lazyLoadFallback);
const { Settings } = loadable(() => import('@/screens/Settings'), lazyLoadFallback);
const { About } = loadable(() => import('@/screens/settings/About'), lazyLoadFallback);
const { Backup } = loadable(() => import('@/screens/settings/Backup'), lazyLoadFallback);
const { Categories } = loadable(() => import('@/screens/settings/Categories'), lazyLoadFallback);
const { DefaultReaderSettings } = loadable(() => import('@/screens/settings/DefaultReaderSettings'), lazyLoadFallback);
const { SourceConfigure } = loadable(() => import('@/screens/SourceConfigure'), lazyLoadFallback);
const { SourceMangas } = loadable(() => import('@/screens/SourceMangas'), lazyLoadFallback);
const { Updates } = loadable(() => import('@/screens/Updates'), lazyLoadFallback);
const { LibrarySettings } = loadable(() => import('@/screens/settings/LibrarySettings'), lazyLoadFallback);
const { DownloadSettings } = loadable(() => import('@/screens/settings/DownloadSettings.tsx'), lazyLoadFallback);
const { ServerSettings } = loadable(() => import('@/screens/settings/ServerSettings.tsx'), lazyLoadFallback);
const { BrowseSettings } = loadable(() => import('@/screens/settings/BrowseSettings.tsx'), lazyLoadFallback);
const { WebUISettings } = loadable(() => import('@/screens/settings/WebUISettings.tsx'), lazyLoadFallback);
const { Migrate } = loadable(() => import('@/screens/Migrate.tsx'), lazyLoadFallback);
const { DeviceSetting } = loadable(() => import('@/components/settings/DeviceSetting.tsx'), lazyLoadFallback);
const { TrackingSettings } = loadable(() => import('@/screens/settings/TrackingSettings.tsx'), lazyLoadFallback);
const { TrackerOAuthLogin } = loadable(() => import('@/screens/TrackerOAuthLogin.tsx'), lazyLoadFallback);
if (process.env.NODE_ENV !== 'production') {
// Adds messages only in a dev environment
@@ -91,6 +95,7 @@ export const App: React.FC = () => (
overflow: 'auto',
}}
>
<ErrorBoundary>
<Routes>
{/* General Routes */}
<Route path="/" element={<Navigate to="/library" replace />} />
@@ -132,10 +137,13 @@ export const App: React.FC = () => (
</Route>
<Route path="tracker/login/oauth" element={<TrackerOAuthLogin />} />
</Routes>
</ErrorBoundary>
</Container>
<ErrorBoundary>
<Routes>
<Route path="manga/:mangaId/chapter/:chapterIndex" element={<Reader />} />
<Route path="*" element={null} />
</Routes>
</ErrorBoundary>
</AppContext>
);

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { Component, ErrorInfo, ReactNode, useEffect, useRef, useState } from 'react';
import { useLocation } from 'react-router-dom';
interface Props {
children?: ReactNode;
setTrackPathChange: (change: boolean) => void;
}
interface State {
error: any;
}
class RealErrorBoundary extends Component<Props, State> {
// eslint-disable-next-line react/state-in-constructor
public state: State = { error: null };
private prevPath: string = '';
public static getDerivedStateFromError(error: any): State {
// Update state so the next render will show the fallback UI.
return { error };
}
componentDidMount() {
this.prevPath = window.location.pathname;
}
public componentDidUpdate() {
if (window.location.pathname !== this.prevPath) {
this.setState({ error: null });
}
this.prevPath = window.location.pathname;
}
public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error('Uncaught error:', error, errorInfo);
// eslint-disable-next-line react/destructuring-assignment
this.props.setTrackPathChange(true);
}
public render() {
const { error } = this.state;
if (error) {
return (
<>
<h1>Something went wrong.</h1>
<p>{error.message ?? JSON.stringify(error)}</p>
</>
);
}
const { children } = this.props;
return children;
}
}
export const ErrorBoundary = ({ children }: { children: React.ReactNode }) => {
const [key, setKey] = useState(0);
const { pathname } = useLocation();
const previousPathnameRef = useRef(pathname);
const [trackPathChange, setTrackPathChange] = useState(false);
useEffect(() => {
if (trackPathChange && previousPathnameRef.current !== pathname) {
previousPathnameRef.current = pathname;
setKey((currentKey) => (currentKey + 1) % 999999);
setTrackPathChange(false);
}
}, [pathname, previousPathnameRef.current, trackPathChange]);
return (
<RealErrorBoundary key={key} setTrackPathChange={setTrackPathChange}>
{children}
</RealErrorBoundary>
);
};

11
src/util/LazyLoad.tsx Normal file
View File

@@ -0,0 +1,11 @@
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder.tsx';
export const lazyLoadFallback = { fallback: <LoadingPlaceholder /> };

View File

@@ -1123,7 +1123,7 @@
dependencies:
regenerator-runtime "^0.14.0"
"@babel/runtime@^7.8.4":
"@babel/runtime@^7.12.18", "@babel/runtime@^7.8.4":
version "7.24.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.4.tgz#de795accd698007a66ba44add6cc86542aff1edd"
integrity sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==
@@ -2049,6 +2049,15 @@
resolved "https://registry.yarnpkg.com/@kamilkisiela/fast-url-parser/-/fast-url-parser-1.1.4.tgz#9d68877a489107411b953c54ea65d0658b515809"
integrity sha512-gbkePEBupNydxCelHCESvFSFM8XPh1Zs/OAVRW/rKpEqPAl5PbOM90Si8mv9bvnR53uPD2s/FiRxdvSejpRJew==
"@loadable/component@^5.16.4":
version "5.16.4"
resolved "https://registry.yarnpkg.com/@loadable/component/-/component-5.16.4.tgz#583cb65945a5061ca3c6b90eb34835c154283ff4"
integrity sha512-fJWxx9b5WHX90QKmizo9B+es2so8DnBthI1mbflwCoOyvzEwxiZ/SVDCTtXEnHG72/kGBdzr297SSIekYtzSOQ==
dependencies:
"@babel/runtime" "^7.12.18"
hoist-non-react-statics "^3.3.1"
react-is "^16.12.0"
"@mui/base@5.0.0-beta.40", "@mui/base@^5.0.0-beta.40":
version "5.0.0-beta.40"
resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.40.tgz#1f8a782f1fbf3f84a961e954c8176b187de3dae2"
@@ -5894,7 +5903,7 @@ react-i18next@14.1.1:
"@babel/runtime" "^7.23.9"
html-parse-stringify "^3.0.1"
react-is@^16.13.1, react-is@^16.7.0:
react-is@^16.12.0, react-is@^16.13.1, react-is@^16.7.0:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
@@ -5909,6 +5918,11 @@ react-is@^18.2.0:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
react-lazily@^0.9.2:
version "0.9.2"
resolved "https://registry.yarnpkg.com/react-lazily/-/react-lazily-0.9.2.tgz#74596dbde43c8e0f607445da5c4839cf6cc48ab5"
integrity sha512-oBVRDQ+SuMPWenBO/0Kq+iZk34lOYJEmjiTto4bYRufndf8pux3E50BT3mJZbsq0vBsAVbX3fpQjlUvsXgDVag==
react-property@2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/react-property/-/react-property-2.0.2.tgz#d5ac9e244cef564880a610bc8d868bd6f60fdda6"