Skip to content

Commit 5a1e466

Browse files
authored
refactor(account-center): global error handler (#8003)
1 parent 8f8e625 commit 5a1e466

File tree

3 files changed

+88
-10
lines changed

3 files changed

+88
-10
lines changed

packages/account-center/src/components/CodeVerification/index.tsx

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { useTranslation } from 'react-i18next';
1313
import LoadingContext from '@ac/Providers/LoadingContextProvider/LoadingContext';
1414
import PageContext from '@ac/Providers/PageContextProvider/PageContext';
1515
import useApi from '@ac/hooks/use-api';
16+
import useErrorHandler from '@ac/hooks/use-error-handler';
1617
import SecondaryPageLayout from '@ac/layouts/SecondaryPageLayout';
1718

1819
import styles from './index.module.scss';
@@ -64,8 +65,8 @@ const CodeVerification = ({
6465
const { loading } = useContext(LoadingContext);
6566
const sendCodeRequest = useApi(sendCode);
6667
const verifyCodeRequest = useApi(verifyCode);
68+
const handleError = useErrorHandler();
6769
const [codeInput, setCodeInput] = useState<string[]>([]);
68-
const [errorMessage, setErrorMessage] = useState<string>();
6970
const [countdown, setCountdown] = useState(0);
7071
const [pendingVerificationRecord, setPendingVerificationRecord] = useState<{
7172
recordId: string;
@@ -111,7 +112,12 @@ const CodeVerification = ({
111112

112113
const [error, result] = await sendCodeRequest(accessToken, identifier);
113114

114-
if (error || !result) {
115+
if (error) {
116+
await handleError(error);
117+
return;
118+
}
119+
120+
if (!result) {
115121
setToast(t('account_center.email_verification.error_send_failed'));
116122
return;
117123
}
@@ -121,10 +127,18 @@ const CodeVerification = ({
121127
expiresAt: result.expiresAt,
122128
});
123129
setCodeInput([]);
124-
setErrorMessage(undefined);
125130
setHasSentCode(true);
126131
startCountdown();
127-
}, [getAccessToken, identifier, loading, sendCodeRequest, setToast, startCountdown, t]);
132+
}, [
133+
getAccessToken,
134+
handleError,
135+
identifier,
136+
loading,
137+
sendCodeRequest,
138+
setToast,
139+
startCountdown,
140+
t,
141+
]);
128142

129143
const handleVerify = useCallback(
130144
async (code: string[]) => {
@@ -154,7 +168,7 @@ const CodeVerification = ({
154168

155169
if (error) {
156170
setCodeInput([]);
157-
setErrorMessage(t('account_center.email_verification.error_verify_failed'));
171+
await handleError(error);
158172
return;
159173
}
160174

@@ -163,11 +177,11 @@ const CodeVerification = ({
163177
},
164178
[
165179
getAccessToken,
180+
handleError,
166181
identifier,
167182
loading,
168183
pendingVerificationRecord,
169184
setVerificationId,
170-
t,
171185
verifyCodeRequest,
172186
]
173187
);
@@ -206,10 +220,8 @@ const CodeVerification = ({
206220
name={codeInputName}
207221
className={styles.codeInput}
208222
value={codeInput}
209-
error={errorMessage}
210223
onChange={(code) => {
211224
setCodeInput(code);
212-
setErrorMessage(undefined);
213225
}}
214226
/>
215227
<div className={styles.message}>
@@ -238,7 +250,7 @@ const CodeVerification = ({
238250
isLoading={loading}
239251
onClick={() => {
240252
if (!isCodeReady(codeInput)) {
241-
setErrorMessage(t('error.invalid_passcode'));
253+
setToast(t('error.invalid_passcode'));
242254
return;
243255
}
244256

packages/account-center/src/components/PasswordVerification/index.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import LoadingContext from '@ac/Providers/LoadingContextProvider/LoadingContext'
88
import PageContext from '@ac/Providers/PageContextProvider/PageContext';
99
import { verifyPassword } from '@ac/apis/verification';
1010
import useApi from '@ac/hooks/use-api';
11+
import useErrorHandler from '@ac/hooks/use-error-handler';
1112
import SecondaryPageLayout from '@ac/layouts/SecondaryPageLayout';
1213

1314
import styles from './index.module.scss';
@@ -23,6 +24,7 @@ const PasswordVerification = ({ onBack }: Props) => {
2324
const { loading } = useContext(LoadingContext);
2425
const [password, setPassword] = useState('');
2526
const asyncVerifyPassword = useApi(verifyPassword);
27+
const handleError = useErrorHandler();
2628

2729
const handleVerify = async (event?: FormEvent<HTMLFormElement>) => {
2830
event?.preventDefault();
@@ -40,7 +42,12 @@ const PasswordVerification = ({ onBack }: Props) => {
4042

4143
const [error, result] = await asyncVerifyPassword(accessToken, password);
4244

43-
if (error || !result) {
45+
if (error) {
46+
await handleError(error);
47+
return;
48+
}
49+
50+
if (!result) {
4451
setToast(t('account_center.password_verification.error_failed'));
4552
return;
4653
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import type { RequestErrorBody } from '@logto/schemas';
2+
import { HTTPError, TimeoutError } from 'ky';
3+
import { useCallback, useContext } from 'react';
4+
import { useTranslation } from 'react-i18next';
5+
6+
import PageContext from '@ac/Providers/PageContextProvider/PageContext';
7+
8+
export type ErrorHandlers = {
9+
[key in RequestErrorBody['code']]?: (error: RequestErrorBody) => void | Promise<void>;
10+
} & {
11+
// Overwrite default global error handle logic
12+
global?: (error: RequestErrorBody) => void | Promise<void>;
13+
};
14+
15+
const useErrorHandler = () => {
16+
const { t } = useTranslation();
17+
const { setToast } = useContext(PageContext);
18+
19+
const handleError = useCallback(
20+
async (error: unknown, errorHandlers?: ErrorHandlers) => {
21+
if (error instanceof HTTPError) {
22+
try {
23+
const logtoError = await error.response.json<RequestErrorBody>();
24+
25+
const { code, message } = logtoError;
26+
27+
const handler = errorHandlers?.[code] ?? errorHandlers?.global;
28+
29+
if (handler) {
30+
await handler(logtoError);
31+
} else {
32+
setToast(message);
33+
}
34+
35+
return;
36+
} catch (error) {
37+
setToast(t('error.unknown'));
38+
console.error(error);
39+
40+
return;
41+
}
42+
}
43+
44+
if (error instanceof TimeoutError) {
45+
setToast(t('error.timeout'));
46+
47+
return;
48+
}
49+
50+
setToast(t('error.unknown'));
51+
console.error(error);
52+
},
53+
[setToast, t]
54+
);
55+
56+
return handleError;
57+
};
58+
59+
export default useErrorHandler;

0 commit comments

Comments
 (0)