diff --git a/idea/frontend/package.json b/idea/frontend/package.json
index 6893f202c5..12d23d3414 100644
--- a/idea/frontend/package.json
+++ b/idea/frontend/package.json
@@ -38,6 +38,7 @@
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dom": "^18.2.0",
+ "react-error-boundary": "^4.0.10",
"react-final-form": "^6.5.9",
"react-final-form-listeners": "^1.0.3",
"react-number-format": "^4.9.3",
diff --git a/idea/frontend/src/app/App.tsx b/idea/frontend/src/app/App.tsx
index c1d859108c..f74d0a8a1c 100644
--- a/idea/frontend/src/app/App.tsx
+++ b/idea/frontend/src/app/App.tsx
@@ -1,5 +1,6 @@
import { useEffect } from 'react';
-import { useSearchParams } from 'react-router-dom';
+import { useLocation, useSearchParams } from 'react-router-dom';
+import { ErrorBoundary } from 'react-error-boundary';
import { useAccount, useApi } from '@gear-js/react-hooks';
import 'simplebar-react/dist/simplebar.min.css';
@@ -11,12 +12,14 @@ import { MobileDisclaimer } from 'widgets/mobileDisclaimer';
import { Routing } from 'pages';
import { LocalStorage, NODE_ADRESS_URL_PARAM } from 'shared/config';
import { Loader } from 'shared/ui/loader';
+import { ErrorFallback } from 'shared/ui/errorFallback';
import { withProviders } from './providers';
import './App.scss';
const App = withProviders(() => {
const { nodeAddress } = useApp();
+ const { pathname } = useLocation();
const [searchParams, setSearchParams] = useSearchParams();
const { api, isApiReady } = useApi();
@@ -52,7 +55,11 @@ const App = withProviders(() => {
- {isAppReady ? : }
+
+ {/* key to reset on route change */}
+
+ {isAppReady ? : }
+
{isMobileDisclaimerVisible && }
diff --git a/idea/frontend/src/hooks/useChangeEffect.ts b/idea/frontend/src/hooks/useChangeEffect.ts
index 527ae69e35..c4cb642b34 100644
--- a/idea/frontend/src/hooks/useChangeEffect.ts
+++ b/idea/frontend/src/hooks/useChangeEffect.ts
@@ -1,16 +1,23 @@
-import { useEffect, useRef } from 'react';
+import { DependencyList, EffectCallback, useEffect, useRef } from 'react';
-export const useChangeEffect = (callback: () => void, dependencies?: unknown[]) => {
- const isMounted = useRef(false);
+export const useChangeEffect = (callback: EffectCallback, dependencies: DependencyList) => {
+ const mounted = useRef(false);
+
+ useEffect(
+ () => () => {
+ mounted.current = false;
+ },
+ [],
+ );
useEffect(() => {
- if (isMounted.current) {
- callback();
+ if (mounted.current) {
+ return callback();
}
- return () => {
- isMounted.current = true;
- };
+ mounted.current = true;
+ return undefined;
+
// eslint-disable-next-line react-hooks/exhaustive-deps
}, dependencies);
};
diff --git a/idea/frontend/src/shared/ui/errorFallback/ErrorFallback.module.scss b/idea/frontend/src/shared/ui/errorFallback/ErrorFallback.module.scss
new file mode 100644
index 0000000000..ca83af1792
--- /dev/null
+++ b/idea/frontend/src/shared/ui/errorFallback/ErrorFallback.module.scss
@@ -0,0 +1,8 @@
+.heading {
+ margin-bottom: 8px;
+}
+
+.error {
+ margin-bottom: 32px;
+ color: #f24a4a;
+}
diff --git a/idea/frontend/src/shared/ui/errorFallback/ErrorFallback.tsx b/idea/frontend/src/shared/ui/errorFallback/ErrorFallback.tsx
new file mode 100644
index 0000000000..c6807bc6b7
--- /dev/null
+++ b/idea/frontend/src/shared/ui/errorFallback/ErrorFallback.tsx
@@ -0,0 +1,16 @@
+import { FallbackProps } from 'react-error-boundary';
+
+import { Subheader } from '../subheader';
+import { BackButton } from '../backButton';
+import styles from './ErrorFallback.module.scss';
+
+const ErrorFallback = ({ error }: FallbackProps) => (
+ <>
+
+ {error.message}
+
+
+ >
+);
+
+export { ErrorFallback };
diff --git a/idea/frontend/src/shared/ui/errorFallback/index.ts b/idea/frontend/src/shared/ui/errorFallback/index.ts
new file mode 100644
index 0000000000..c69b334873
--- /dev/null
+++ b/idea/frontend/src/shared/ui/errorFallback/index.ts
@@ -0,0 +1,3 @@
+import { ErrorFallback } from './ErrorFallback';
+
+export { ErrorFallback };
diff --git a/yarn.lock b/yarn.lock
index 92269f5bf7..9f7844d869 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2204,6 +2204,7 @@ __metadata:
react-dnd: ^16.0.1
react-dnd-html5-backend: ^16.0.1
react-dom: ^18.2.0
+ react-error-boundary: ^4.0.10
react-final-form: ^6.5.9
react-final-form-listeners: ^1.0.3
react-number-format: ^4.9.3
@@ -16743,6 +16744,17 @@ __metadata:
languageName: node
linkType: hard
+"react-error-boundary@npm:^4.0.10":
+ version: 4.0.10
+ resolution: "react-error-boundary@npm:4.0.10"
+ dependencies:
+ "@babel/runtime": ^7.12.5
+ peerDependencies:
+ react: ">=16.13.1"
+ checksum: 4ad4864d2a5fc2264a24d03e83176e6a70d7adbe3c1edbdc5b0bd452a695104bc59456e23b5aea1b9729220672fe46614221daa8b3bd59327968d4aa7eb8bc71
+ languageName: node
+ linkType: hard
+
"react-error-overlay@npm:^6.0.11":
version: 6.0.11
resolution: "react-error-overlay@npm:6.0.11"