diff --git a/web/ui/react-app/src/App.test.tsx b/web/ui/react-app/src/App.test.tsx
index e96bbf7a40..6ccdf9ffe1 100755
--- a/web/ui/react-app/src/App.test.tsx
+++ b/web/ui/react-app/src/App.test.tsx
@@ -4,7 +4,17 @@ import App from './App';
import Navigation from './Navbar';
import { Container } from 'reactstrap';
import { Router } from '@reach/router';
-import { Alerts, Config, Flags, Rules, ServiceDiscovery, Status, Targets, TSDBStatus, PanelList } from './pages';
+import {
+ AlertsPage,
+ ConfigPage,
+ FlagsPage,
+ RulesPage,
+ ServiceDiscoveryPage,
+ StatusPage,
+ TargetsPage,
+ TSDBStatusPage,
+ PanelListPage,
+} from './pages';
describe('App', () => {
const app = shallow();
@@ -13,7 +23,17 @@ describe('App', () => {
expect(app.find(Navigation)).toHaveLength(1);
});
it('routes', () => {
- [Alerts, Config, Flags, Rules, ServiceDiscovery, Status, Targets, TSDBStatus, PanelList].forEach(component => {
+ [
+ AlertsPage,
+ ConfigPage,
+ FlagsPage,
+ RulesPage,
+ ServiceDiscoveryPage,
+ StatusPage,
+ TargetsPage,
+ TSDBStatusPage,
+ PanelListPage,
+ ].forEach(component => {
const c = app.find(component);
expect(c).toHaveLength(1);
});
diff --git a/web/ui/react-app/src/App.tsx b/web/ui/react-app/src/App.tsx
index 143b3d3ce2..9370ca48ad 100755
--- a/web/ui/react-app/src/App.tsx
+++ b/web/ui/react-app/src/App.tsx
@@ -2,15 +2,23 @@ import React, { FC } from 'react';
import Navigation from './Navbar';
import { Container } from 'reactstrap';
-import { Router, Redirect, navigate } from '@reach/router';
+import { Router, Redirect } from '@reach/router';
import useMedia from 'use-media';
-import { Alerts, Config, Flags, Rules, ServiceDiscovery, Status, Targets, TSDBStatus, PanelList, Starting } from './pages';
+import {
+ AlertsPage,
+ ConfigPage,
+ FlagsPage,
+ RulesPage,
+ ServiceDiscoveryPage,
+ StatusPage,
+ TargetsPage,
+ TSDBStatusPage,
+ PanelListPage,
+} from './pages';
import { PathPrefixContext } from './contexts/PathPrefixContext';
import { ThemeContext, themeName, themeSetting } from './contexts/ThemeContext';
import { Theme, themeLocalStorageKey } from './Theme';
import { useLocalStorage } from './hooks/useLocalStorage';
-import { useFetchReady } from './hooks/useFetch';
-import { usePathPrefix } from './contexts/PathPrefixContext';
interface AppProps {
consolesLink: string | null;
@@ -31,7 +39,6 @@ const App: FC = ({ consolesLink }) => {
'/rules',
'/targets',
'/service-discovery',
- '/starting',
];
if (basePath.endsWith('/')) {
basePath = basePath.slice(0, -1);
@@ -45,14 +52,6 @@ const App: FC = ({ consolesLink }) => {
}
}
- const pathPrefix = usePathPrefix();
- const { ready, isLoading, isUnexpected } = useFetchReady(pathPrefix);
- if (basePath !== '/starting') {
- if (!ready && !isLoading && !isUnexpected) {
- navigate('/starting');
- }
- }
-
const [userTheme, setUserTheme] = useLocalStorage(themeLocalStorageKey, 'auto');
const browserHasThemes = useMedia('(prefers-color-scheme)');
const browserWantsDarkTheme = useMedia('(prefers-color-scheme: dark)');
@@ -78,16 +77,15 @@ const App: FC = ({ consolesLink }) => {
NOTE: Any route added here needs to also be added to the list of
React-handled router paths ("reactRouterPaths") in /web/web.go.
*/}
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/web/ui/react-app/src/pages/starting/Starting.test.tsx b/web/ui/react-app/src/components/withStartingIndicator.test.tsx
similarity index 92%
rename from web/ui/react-app/src/pages/starting/Starting.test.tsx
rename to web/ui/react-app/src/components/withStartingIndicator.test.tsx
index 80fc814306..ec4fd25f3a 100644
--- a/web/ui/react-app/src/pages/starting/Starting.test.tsx
+++ b/web/ui/react-app/src/components/withStartingIndicator.test.tsx
@@ -1,7 +1,7 @@
import * as React from 'react';
import { shallow } from 'enzyme';
-import { WALReplayData } from '../../types/types';
-import { StartingContent } from './Starting';
+import { WALReplayData } from '../types/types';
+import { StartingContent } from './withStartingIndicator';
import { Progress } from 'reactstrap';
describe('Starting', () => {
diff --git a/web/ui/react-app/src/pages/starting/Starting.tsx b/web/ui/react-app/src/components/withStartingIndicator.tsx
similarity index 73%
rename from web/ui/react-app/src/pages/starting/Starting.tsx
rename to web/ui/react-app/src/components/withStartingIndicator.tsx
index 936fea050c..a935913a01 100644
--- a/web/ui/react-app/src/pages/starting/Starting.tsx
+++ b/web/ui/react-app/src/components/withStartingIndicator.tsx
@@ -1,10 +1,9 @@
-import React, { FC, useEffect } from 'react';
-import { RouteComponentProps, navigate } from '@reach/router';
+import React, { FC, ComponentType } from 'react';
import { Progress, Alert } from 'reactstrap';
-import { useFetchReadyInterval } from '../../hooks/useFetch';
-import { WALReplayData } from '../../types/types';
-import { usePathPrefix } from '../../contexts/PathPrefixContext';
+import { useFetchReadyInterval } from '../hooks/useFetch';
+import { WALReplayData } from '../types/types';
+import { usePathPrefix } from '../contexts/PathPrefixContext';
interface StartingContentProps {
isUnexpected: boolean;
@@ -44,17 +43,13 @@ export const StartingContent: FC = ({ status, isUnexpected
);
};
-const Starting: FC = () => {
+export const withStartingIndicator = (Page: ComponentType): FC => ({ ...rest }) => {
const pathPrefix = usePathPrefix();
const { ready, walReplayStatus, isUnexpected } = useFetchReadyInterval(pathPrefix);
- useEffect(() => {
- if (ready) {
- navigate('/');
- }
- }, [ready]);
+ if (ready || isUnexpected) {
+ return ;
+ }
return ;
};
-
-export default Starting;
diff --git a/web/ui/react-app/src/hooks/useFetch.ts b/web/ui/react-app/src/hooks/useFetch.ts
index f88b01c0f0..52e5a5d4b8 100644
--- a/web/ui/react-app/src/hooks/useFetch.ts
+++ b/web/ui/react-app/src/hooks/useFetch.ts
@@ -47,36 +47,7 @@ export const useFetch = (url: string, options?: RequestInit): Fetc
return { response, error, isLoading };
};
-export const useFetchReady = (pathPrefix: string, options?: RequestInit): FetchStateReady => {
- const [ready, setReady] = useState(false);
- const [isUnexpected, setIsUnexpected] = useState(false);
- const [isLoading, setIsLoading] = useState(true);
-
- useEffect(() => {
- const fetchData = async () => {
- setIsLoading(true);
- try {
- const res = await fetch(`${pathPrefix}/-/ready`, { cache: 'no-store', credentials: 'same-origin', ...options });
- if (res.status === 200) {
- setReady(true);
- }
- // The server sends back a 503 if it isn't ready,
- // if we get back anything else that means something has gone wrong.
- if (res.status !== 503) {
- setIsUnexpected(true);
- } else {
- setIsUnexpected(false);
- }
-
- setIsLoading(false);
- } catch (error) {
- setIsUnexpected(true);
- }
- };
- fetchData();
- }, [pathPrefix, options]);
- return { ready, isUnexpected, isLoading };
-};
+let wasReady = false;
// This is used on the starting page to periodically check if the server is ready yet,
// and check the status of the WAL replay.
@@ -86,33 +57,60 @@ export const useFetchReadyInterval = (pathPrefix: string, options?: RequestInit)
const [walReplayStatus, setWALReplayStatus] = useState({} as any);
useEffect(() => {
- const interval = setInterval(async () => {
- try {
- let res = await fetch(`${pathPrefix}/-/ready`, { cache: 'no-store', credentials: 'same-origin', ...options });
- if (res.status === 200) {
- setReady(true);
+ if (wasReady) {
+ setReady(true);
+ } else {
+ // This helps avoid a memory leak.
+ let mounted = true;
+
+ const fetchStatus = async () => {
+ try {
+ let res = await fetch(`${pathPrefix}/-/ready`, { cache: 'no-store', credentials: 'same-origin', ...options });
+ if (res.status === 200) {
+ if (mounted) {
+ setReady(true);
+ }
+ wasReady = true;
+ clearInterval(interval);
+ } else if (res.status !== 503) {
+ if (mounted) {
+ setIsUnexpected(true);
+ }
+ clearInterval(interval);
+ return;
+ } else {
+ if (mounted) {
+ setIsUnexpected(false);
+ }
+
+ res = await fetch(`${pathPrefix}/${API_PATH}/status/walreplay`, {
+ cache: 'no-store',
+ credentials: 'same-origin',
+ });
+ if (res.ok) {
+ const data = (await res.json()) as WALReplayStatus;
+ if (mounted) {
+ setWALReplayStatus(data);
+ }
+ }
+ }
+ } catch (error) {
+ if (mounted) {
+ setIsUnexpected(true);
+ }
clearInterval(interval);
return;
}
- if (res.status !== 503) {
- setIsUnexpected(true);
- setWALReplayStatus({ data: { last: 0, first: 0 } } as any);
- } else {
- setIsUnexpected(false);
+ };
- res = await fetch(`${pathPrefix}/${API_PATH}/status/walreplay`, { cache: 'no-store', credentials: 'same-origin' });
- if (res.ok) {
- const data = (await res.json()) as WALReplayStatus;
- setWALReplayStatus(data);
- }
- }
- } catch (error) {
- setIsUnexpected(true);
- setWALReplayStatus({ data: { last: 0, first: 0 } } as any);
- }
- }, 1000);
-
- return () => clearInterval(interval);
+ fetchStatus();
+ const interval = setInterval(fetchStatus, 1000);
+ return () => {
+ clearInterval(interval);
+ mounted = false;
+ };
+ }
}, [pathPrefix, options]);
+
return { ready, isUnexpected, walReplayStatus };
};
diff --git a/web/ui/react-app/src/pages/index.ts b/web/ui/react-app/src/pages/index.ts
index d8ad5e72be..c514663375 100644
--- a/web/ui/react-app/src/pages/index.ts
+++ b/web/ui/react-app/src/pages/index.ts
@@ -7,6 +7,27 @@ import Status from './status/Status';
import Targets from './targets/Targets';
import PanelList from './graph/PanelList';
import TSDBStatus from './tsdbStatus/TSDBStatus';
-import Starting from './starting/Starting';
+import { withStartingIndicator } from '../components/withStartingIndicator';
-export { Alerts, Config, Flags, Rules, ServiceDiscovery, Status, Targets, TSDBStatus, PanelList, Starting };
+const AlertsPage = withStartingIndicator(Alerts);
+const ConfigPage = withStartingIndicator(Config);
+const FlagsPage = withStartingIndicator(Flags);
+const RulesPage = withStartingIndicator(Rules);
+const ServiceDiscoveryPage = withStartingIndicator(ServiceDiscovery);
+const StatusPage = withStartingIndicator(Status);
+const TSDBStatusPage = withStartingIndicator(TSDBStatus);
+const TargetsPage = withStartingIndicator(Targets);
+const PanelListPage = withStartingIndicator(PanelList);
+
+// prettier-ignore
+export {
+ AlertsPage,
+ ConfigPage,
+ FlagsPage,
+ RulesPage,
+ ServiceDiscoveryPage,
+ StatusPage,
+ TSDBStatusPage,
+ TargetsPage,
+ PanelListPage
+};