Skip to content

Commit

Permalink
improve: handle unhandled exception
Browse files Browse the repository at this point in the history
  • Loading branch information
w-okada committed Jul 23, 2023
1 parent a8a392b commit 7200896
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 1,229 deletions.
11 changes: 1 addition & 10 deletions client/demo/dist/index.html
Original file line number Diff line number Diff line change
@@ -1,10 +1 @@
<!DOCTYPE html>
<html style="width: 100%; height: 100%; overflow: hidden">
<head>
<meta charset="utf-8" />
<title>Voice Changer Client Demo</title>
<script defer src="index.js"></script></head>
<body style="width: 100%; height: 100%; margin: 0px">
<div id="app" style="width: 100%; height: 100%"></div>
</body>
</html>
<!doctype html><html style="width:100%;height:100%;overflow:hidden"><head><meta charset="utf-8"/><title>Voice Changer Client Demo</title><script defer="defer" src="index.js"></script></head><body style="width:100%;height:100%;margin:0"><div id="app" style="width:100%;height:100%"></div></body></html>
1,147 changes: 2 additions & 1,145 deletions client/demo/dist/index.js

Large diffs are not rendered by default.

31 changes: 31 additions & 0 deletions client/demo/dist/index.js.LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */

/**
* @license React
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/**
* @license React
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
128 changes: 66 additions & 62 deletions client/demo/src/000_index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { library } from "@fortawesome/fontawesome-svg-core";
import { fas } from "@fortawesome/free-solid-svg-icons";
import { far } from "@fortawesome/free-regular-svg-icons";
import { fab } from "@fortawesome/free-brands-svg-icons";
import { ErrorInfo, useEffect, useMemo, useState, } from "react";
import { ErrorInfo, useEffect, useMemo, useState } from "react";

import "./css/App.css"
import "./css/App.css";
import ErrorBoundary from "./001_provider/900_ErrorBoundary";
import { AppStateProvider } from "./001_provider/001_AppStateProvider";
import { AppRootProvider, useAppRoot } from "./001_provider/001_AppRootProvider";
Expand All @@ -16,117 +16,121 @@ import { useMessageBuilder } from "./hooks/useMessageBuilder";

library.add(fas, far, fab);


const container = document.getElementById("app")!;
const root = createRoot(container);

const App = () => {
const { appGuiSettingState } = useAppRoot()
const { appGuiSettingState } = useAppRoot();
const front = useMemo(() => {
if (appGuiSettingState.appGuiSetting.type == "demo") {
return <Demo></Demo>
return <Demo></Demo>;
} else {
return <>unknown gui type. {appGuiSettingState.appGuiSetting.type}</>
return <>unknown gui type. {appGuiSettingState.appGuiSetting.type}</>;
}
}, [appGuiSettingState.appGuiSetting.type])
}, [appGuiSettingState.appGuiSetting.type]);

return (
<>
{front}
</>
)
}
return <>{front}</>;
};

const AppStateWrapper = () => {
const { appGuiSettingState, getGUISetting } = useAppRoot()
const messageBuilderState = useMessageBuilder()
const { appGuiSettingState, getGUISetting } = useAppRoot();
const messageBuilderState = useMessageBuilder();
// エラーメッセージ登録
useMemo(() => {
messageBuilderState.setMessage(__filename, "Problem", { "ja": "ちょっと問題が起きたみたいです。", "en": "Looks like there's a bit of a problem." })
messageBuilderState.setMessage(__filename, "Problem-sub1", { "ja": "このアプリで管理している情報をクリアすると回復する場合があります。", "en": "" })
messageBuilderState.setMessage(__filename, "Problem-sub2", { "ja": "下記のボタンを押して情報をクリアします。", "en": "If you clear the information being managed by this app, it may be recoverable." })
messageBuilderState.setMessage(__filename, "Problem-action1", { "ja": "アプリを初期化", "en": "Initialize" })
messageBuilderState.setMessage(__filename, "Problem-action2", { "ja": "初期化せずリロード", "en": "Reload without initialize" })
}, [])
messageBuilderState.setMessage(__filename, "Problem", { ja: "ちょっと問題が起きたみたいです。", en: "Looks like there's a bit of a problem." });
messageBuilderState.setMessage(__filename, "Problem-sub1", { ja: "このアプリで管理している情報をクリアすると回復する場合があります。", en: "" });
messageBuilderState.setMessage(__filename, "Problem-sub2", { ja: "下記のボタンを押して情報をクリアします。", en: "If you clear the information being managed by this app, it may be recoverable." });
messageBuilderState.setMessage(__filename, "Problem-action1", { ja: "アプリを初期化", en: "Initialize" });
messageBuilderState.setMessage(__filename, "Problem-action2", { ja: "初期化せずリロード", en: "Reload without initialize" });
}, []);

// エラーバウンダリー設定
const [error, setError] = useState<{ error: Error, errorInfo: ErrorInfo }>()
const { removeDB } = useIndexedDB({ clientType: null })

const [error, setError] = useState<{ error: Error; errorInfo: ErrorInfo | null; reason: any }>();
const { removeDB } = useIndexedDB({ clientType: null });

const errorComponent = useMemo(() => {
const errorName = error?.error.name || "no error name"
const errorMessage = error?.error.message || "no error message"
const errorInfos = (error?.errorInfo.componentStack || "no error stack").split("\n")
const errorName = error?.error.name || "no error name";
const errorMessage = error?.error.message || "no error message";
const errorInfos = (error?.errorInfo?.componentStack || "no error stack").split("\n");
const reasonMessage = (error?.reason || "no reason").toString();
const reasonStack = (error?.reason.stack || "no stack").split("\n") as string[];

const onClearCacheClicked = async () => {
await removeDB()
await removeDB();
location.reload();
}
};
const onReloadClicked = () => {
location.reload();
}
};
return (
<div className="error-container">
<div className="top-error-message">
{messageBuilderState.getMessage(__filename, "Problem")}
</div>
<div className="top-error-message">{messageBuilderState.getMessage(__filename, "Problem")}</div>
<div className="top-error-description">
<p> {messageBuilderState.getMessage(__filename, "Problem-sub1")}</p>
<p> {messageBuilderState.getMessage(__filename, "Problem-sub2")}</p>
<p><button onClick={onClearCacheClicked}>{messageBuilderState.getMessage(__filename, "Problem-action1")}</button></p>
<p><button onClick={onReloadClicked}>{messageBuilderState.getMessage(__filename, "Problem-action2")}</button></p>
<p>
<button onClick={onClearCacheClicked}>{messageBuilderState.getMessage(__filename, "Problem-action1")}</button>
</p>
<p>
<button onClick={onReloadClicked}>{messageBuilderState.getMessage(__filename, "Problem-action2")}</button>
</p>
</div>
<div className="error-detail">
<div className="error-name">
{errorName}
</div>
<div className="error-message">
{errorMessage}
<div className="error-name">{errorName}</div>
<div className="error-message">{errorMessage}</div>
<div className="error-info-container">
{errorInfos.map((x) => {
return (
<div className="error-info-line" key={x}>
{x}
</div>
);
})}
</div>
</div>
<div className="error-detail">
<div className="error-name">{reasonMessage}</div>
<div className="error-message"></div>
<div className="error-info-container">
{errorInfos.map(x => {
return <div className="error-info-line" key={x}>{x}</div>
{reasonStack.map((x) => {
return (
<div className="error-info-line" key={x}>
{x}
</div>
);
})}
</div>

</div>
</div>
)
}, [error])

const updateError = (error: Error, errorInfo: React.ErrorInfo) => {
console.log("error compo", error, errorInfo)
setError({ error, errorInfo })
}
);
}, [error]);

const updateError = (error: Error, errorInfo: React.ErrorInfo | null, reason: any) => {
setError({ error, errorInfo, reason });
};

useEffect(() => {
const loadDefaultModelType = async () => {
getGUISetting()
}
loadDefaultModelType()
}, [])


getGUISetting();
};
loadDefaultModelType();
}, []);

if (!appGuiSettingState.guiSettingLoaded) {
return <>loading...</>
return <>loading...</>;
} else {
return (
<ErrorBoundary fallback={errorComponent} onError={updateError}>
<AppStateProvider>
<App></App>
</AppStateProvider>
</ErrorBoundary>
)
);
}

}
};

root.render(
<AppRootProvider>
<AppStateWrapper></AppStateWrapper>
</AppRootProvider>
</AppRootProvider>,
);

30 changes: 18 additions & 12 deletions client/demo/src/001_provider/900_ErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import React, { ErrorInfo } from 'react';
import React, { ErrorInfo } from "react";

type ErrorBoundaryProps = {
children: React.ReactNode;
fallback: React.ReactNode;
onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
}
onError: (error: Error, errorInfo: React.ErrorInfo | null, reason: any) => void;
};

type ErrorBoundaryState = {
hasError: boolean;
}
};

class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
private eventHandler: () => void
private eventHandler: () => void;
constructor(props: ErrorBoundaryProps) {
super(props);
this.state = { hasError: false };
Expand All @@ -24,24 +24,31 @@ class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundarySta
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
// For logging
console.warn("React Error Boundary Catch", error, errorInfo)
console.warn("React Error Boundary Catch", error, errorInfo);
const { onError } = this.props;
if (onError) {
onError(error, errorInfo);
onError(error, errorInfo, null);
}
}


// 非同期例外対応
updateError() {
this.setState({ hasError: true });
}
handledRejection = (event: PromiseRejectionEvent) => {
const { onError } = this.props;
const error = new Error(event.type);
onError(error, null, event.reason);
this.setState({ hasError: true });
};
componentDidMount() {
window.addEventListener('unhandledrejection', this.eventHandler)
// window.addEventListener('unhandledrejection', this.eventHandler)
window.addEventListener("unhandledrejection", this.handledRejection);
}

componentWillUnmount() {
window.removeEventListener('unhandledrejection', this.eventHandler)
// window.removeEventListener('unhandledrejection', this.eventHandler)
window.removeEventListener("unhandledrejection", this.handledRejection);
}

render() {
Expand All @@ -52,5 +59,4 @@ class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundarySta
}
}


export default ErrorBoundary;
export default ErrorBoundary;

0 comments on commit 7200896

Please sign in to comment.