improve: handle unhandled exception

This commit is contained in:
w-okada 2023-07-23 23:56:37 +09:00
parent a8a392b20d
commit 7200896940
5 changed files with 119 additions and 1230 deletions

View File

@ -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>

File diff suppressed because one or more lines are too long

31
client/demo/dist/index.js.LICENSE.txt vendored Normal file
View File

@ -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.
*/

View File

@ -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";
@ -16,102 +16,108 @@ 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>
<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>
{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">
{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}>
@ -119,14 +125,12 @@ const AppStateWrapper = () => {
<App></App>
</AppStateProvider>
</ErrorBoundary>
)
);
}
}
};
root.render(
<AppRootProvider>
<AppStateWrapper></AppStateWrapper>
</AppRootProvider>
</AppRootProvider>,
);

View File

@ -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 };
@ -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() {
@ -52,5 +59,4 @@ class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundarySta
}
}
export default ErrorBoundary;
export default ErrorBoundary;