Skip to content

Commit 9fb637b

Browse files
committed
refactor(platform): refactor useHttp
1 parent 6301b20 commit 9fb637b

File tree

11 files changed

+154
-80
lines changed

11 files changed

+154
-80
lines changed

packages/platform/src/app/App.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export type AppTheme = 'light' | 'dark';
1818

1919
export function App() {
2020
const { i18n } = useTranslation();
21-
const createHttp = useHttp();
21+
const http = useHttp();
2222
const init = useInit();
2323
const navigate = useNavigate();
2424
const async = useAsync();
@@ -29,11 +29,11 @@ export function App() {
2929
useMount(() => {
3030
i18n.changeLanguage(languageStorage.value);
3131

32-
const [http] = createHttp();
33-
http<UserState>({
34-
url: '/api/auth/me',
32+
const [authReq] = http<UserState>({
33+
url: '/auth/me',
3534
method: 'get',
36-
}).subscribe({
35+
});
36+
authReq.subscribe({
3737
next: (res) => {
3838
setLoading(false);
3939
init(res);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
export { useAPI } from './useAPI';
12
export { usePageTitle } from './usePageTitle';
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// https://cloud.google.com/apis/design
2+
3+
import { useEventCallback } from '@react-devui/hooks';
4+
5+
import { useHttp } from '../../core';
6+
7+
export interface StandardFields {
8+
create_time: number;
9+
update_time: number;
10+
}
11+
12+
export interface ErrorStandardFields {
13+
error: {
14+
code: string;
15+
message: string;
16+
};
17+
}
18+
19+
export function useAPI(url: string) {
20+
const http = useHttp();
21+
22+
return {
23+
list: useEventCallback(<T extends StandardFields = any, D = any>(data?: D) =>
24+
http<T[], D>({
25+
url,
26+
method: 'get',
27+
data,
28+
})
29+
),
30+
get: useEventCallback(<T extends StandardFields = any, D = any>(id: number, data?: D) =>
31+
http<T, D>({
32+
url: `${url}/${id}`,
33+
method: 'get',
34+
data,
35+
})
36+
),
37+
create: useEventCallback(<T extends StandardFields = any, D = any>(data: D) =>
38+
http<T, D>({
39+
url,
40+
method: 'post',
41+
data,
42+
})
43+
),
44+
update: useEventCallback(<T extends StandardFields = any, D = any>(id: number, data: D) =>
45+
http<T, D>({
46+
url: `${url}/${id}`,
47+
method: 'put',
48+
data,
49+
})
50+
),
51+
delete: useEventCallback((id: number) =>
52+
http<Record<string, never>>({
53+
url: `${url}/${id}`,
54+
method: 'delete',
55+
})
56+
),
57+
};
58+
}

packages/platform/src/app/routes/login/Login.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { BASE64_DATA } from './base64.out';
1717

1818
export default function Login(): JSX.Element | null {
1919
const { t } = useTranslation();
20-
const createHttp = useHttp();
20+
const http = useHttp();
2121
const [loginloading, setLoginLoading] = useState(false);
2222
const init = useInit();
2323
const async = useAsync();
@@ -57,13 +57,13 @@ export default function Login(): JSX.Element | null {
5757
);
5858

5959
const handleSubmit = () => {
60-
const [http] = createHttp();
6160
setLoginLoading(true);
62-
http<{ user: UserState; token: string }>({
63-
url: '/api/login',
61+
const [loginReq] = http<{ user: UserState; token: string }>({
62+
url: '/login',
6463
method: 'post',
6564
data: { username: accountForm.get('username').value },
66-
}).subscribe({
65+
});
66+
loginReq.subscribe({
6767
next: (res) => {
6868
setLoginLoading(false);
6969
TOKEN.set(res.token);

packages/platform/src/app/routes/test/http/Http.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { AppRouteHeader } from '../../../components';
55
import styles from './Http.module.scss';
66

77
export default function Http(): JSX.Element | null {
8-
const createHttp = useHttp();
8+
const http = useHttp();
99

1010
return (
1111
<>
@@ -21,12 +21,12 @@ export default function Http(): JSX.Element | null {
2121
<DButton
2222
key={status}
2323
onClick={() => {
24-
const [http] = createHttp();
25-
http({
26-
url: '/api/test/http',
24+
const [testReq] = http({
25+
url: '/test/http',
2726
method: 'post',
2827
data: { status },
29-
}).subscribe();
28+
});
29+
testReq.subscribe();
3030
}}
3131
>
3232
{status}

packages/platform/src/core/http/mock.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { ROUTES_ACL } from '../../config/acl';
99
import { environment } from '../../environments';
1010
import { TOKEN } from '../token';
1111

12-
if (environment.mock) {
12+
if (environment.http.mock) {
1313
const mock = new MockAdapter(axios);
1414
const withDelay =
1515
<T>(delay: number, response: T) =>
@@ -51,10 +51,10 @@ if (environment.mock) {
5151
},
5252
];
5353

54-
mock.onGet('/api/notification').reply(withDelay(500, [200, notification]));
54+
mock.onGet(environment.http.transformURL('/notification')).reply(withDelay(500, [200, notification]));
5555

5656
mock
57-
.onGet('/api/auth/me')
57+
.onGet(environment.http.transformURL('/auth/me'))
5858
.reply(withDelay(500, [200, (TOKEN as JWTToken<JWTTokenPayload & { admin: boolean }>).payload?.admin ? admin : user]));
5959

6060
for (const username of ['admin', 'user']) {
@@ -78,7 +78,7 @@ if (environment.mock) {
7878
});
7979
}
8080

81-
mock.onPost('/api/auth/refresh').reply(() => {
81+
mock.onPost(environment.http.transformURL('/auth/refresh')).reply(() => {
8282
return new Promise((resolve) => {
8383
setTimeout(() => {
8484
resolve([
@@ -95,6 +95,6 @@ if (environment.mock) {
9595
});
9696

9797
for (const status of [401, 403, 404, 500]) {
98-
mock.onPost('/api/test/http', { status }).reply(status);
98+
mock.onPost(environment.http.transformURL('/test/http'), { status }).reply(status);
9999
}
100100
}

packages/platform/src/core/http/useHttp.ts

Lines changed: 42 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export function useHttp() {
3232
}
3333
});
3434

35-
return useEventCallback((options?: { unmount?: boolean }) => {
35+
return useEventCallback(<T = any, D = any>(config: AxiosRequestConfig<D>, options?: { unmount?: boolean }) => {
3636
const { unmount = true } = options ?? {};
3737

3838
const onDestroy$ = new Subject<void>();
@@ -46,52 +46,51 @@ export function useHttp() {
4646
dataRef.current.abortFns.add(abort);
4747
}
4848

49-
return [
50-
<T = any, D = any>(config: AxiosRequestConfig<D>) => {
51-
const headers = { ...config.headers };
52-
if (!isNull(TOKEN.value)) {
53-
headers['Authorization'] = `Bearer ${TOKEN.value}`;
54-
}
49+
const headers = { ...config.headers };
50+
if (!isNull(TOKEN.value)) {
51+
headers['Authorization'] = `Bearer ${TOKEN.value}`;
52+
}
5553

56-
return from(
57-
axios({
58-
...config,
59-
baseURL: environment.baseURL,
60-
headers,
61-
signal: controller.signal,
62-
}) as Promise<AxiosResponse<T, D>>
63-
).pipe(
64-
takeUntil(onDestroy$),
65-
catchError((error: AxiosError<T, D>) => {
66-
if (error.response) {
67-
switch (error.response.status) {
68-
case 401:
69-
ToastService.open({
70-
dContent: t('User not authorized'),
71-
dType: 'error',
72-
});
73-
navigate(LOGIN_PATH, { state: { [PREV_ROUTE_KEY]: location } });
74-
return EMPTY;
54+
return [
55+
from(
56+
axios({
57+
...config,
58+
baseURL: environment.http.baseURL,
59+
url: environment.http.transformURL(config.url!),
60+
headers,
61+
signal: controller.signal,
62+
}) as Promise<AxiosResponse<T, D>>
63+
).pipe(
64+
takeUntil(onDestroy$),
65+
catchError((error: AxiosError<T, D>) => {
66+
if (error.response) {
67+
switch (error.response.status) {
68+
case 401:
69+
ToastService.open({
70+
dContent: t('User not authorized'),
71+
dType: 'error',
72+
});
73+
navigate(LOGIN_PATH, { state: { [PREV_ROUTE_KEY]: location } });
74+
return EMPTY;
7575

76-
case 403:
77-
case 404:
78-
case 500:
79-
navigate(`/exception/${error.response.status}`);
80-
return EMPTY;
76+
case 403:
77+
case 404:
78+
case 500:
79+
navigate(`/exception/${error.response.status}`);
80+
return EMPTY;
8181

82-
default:
83-
break;
84-
}
85-
} else if (error.request) {
86-
// The request was made but no response was received.
87-
} else {
88-
// Something happened in setting up the request that triggered an Error.
82+
default:
83+
break;
8984
}
90-
return throwError(() => error);
91-
}),
92-
map((res) => res.data)
93-
);
94-
},
85+
} else if (error.request) {
86+
// The request was made but no response was received.
87+
} else {
88+
// Something happened in setting up the request that triggered an Error.
89+
}
90+
return throwError(() => error);
91+
}),
92+
map((res) => res.data)
93+
),
9594
abort,
9695
] as const;
9796
});

packages/platform/src/core/token/useRefreshToken.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,23 @@ import { TOKEN_REFRESH, TOKEN, TOKEN_REFRESH_OFFSET } from './token';
66
let CLEAR_TOKEN_REFRESH: (() => void) | undefined;
77

88
export function useRefreshToken() {
9-
const createHttp = useHttp();
9+
const http = useHttp();
1010

1111
return () => {
1212
CLEAR_TOKEN_REFRESH?.();
1313
if (TOKEN_REFRESH) {
1414
const refresh = () => {
1515
const expiration = TOKEN.expiration;
1616
if (!isNull(expiration) && !TOKEN.expired) {
17-
const [http, abort] = createHttp({ unmount: false });
18-
const tid = window.setTimeout(() => {
19-
http<string>({
20-
url: '/api/auth/refresh',
17+
const [refreshTokenReq, abortRefreshTokenReq] = http<string>(
18+
{
19+
url: '/auth/refresh',
2120
method: 'post',
22-
}).subscribe({
21+
},
22+
{ unmount: false }
23+
);
24+
const tid = window.setTimeout(() => {
25+
refreshTokenReq.subscribe({
2326
next: (res) => {
2427
TOKEN.set(res);
2528
refresh();
@@ -28,7 +31,7 @@ export function useRefreshToken() {
2831
}, expiration - Date.now() - TOKEN_REFRESH_OFFSET);
2932
CLEAR_TOKEN_REFRESH = () => {
3033
clearTimeout(tid);
31-
abort();
34+
abortRefreshTokenReq();
3235
};
3336
}
3437
};

packages/platform/src/core/useInit.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { useACL } from './useACL';
1111
import { useMenu } from './useMenu';
1212

1313
export function useInit() {
14-
const createHttp = useHttp();
14+
const http = useHttp();
1515
const acl = useACL();
1616

1717
const [, setUser] = useUserState();
@@ -64,11 +64,14 @@ export function useInit() {
6464

6565
const getNotification = () => {
6666
setNotification(undefined);
67-
const [http] = createHttp({ unmount: false });
68-
http<NotificationItem[]>({
69-
url: '/api/notification',
70-
method: 'get',
71-
}).subscribe({
67+
const [notificationReq] = http<NotificationItem[]>(
68+
{
69+
url: '/notification',
70+
method: 'get',
71+
},
72+
{ unmount: false }
73+
);
74+
notificationReq.subscribe({
7275
next: (res) => {
7376
setNotification(res);
7477
},
Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
export const environment = {
22
production: true,
3-
baseURL: 'https://example.com',
4-
mock: true,
3+
http: {
4+
mock: true,
5+
baseURL: 'https://example.com',
6+
transformURL: (url: string) => {
7+
return '/api/v1' + url;
8+
},
9+
},
510
};

0 commit comments

Comments
 (0)