Skip to content

Commit e35b633

Browse files
deduplicate some functions & types
1 parent af17f11 commit e35b633

11 files changed

+410
-346
lines changed

package-lock.json

Lines changed: 18 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"react-window": "^1.8.10",
3333
"reconnecting-websocket": "^4.4.0",
3434
"redux": "^5.0.1",
35+
"type-fest": "^4.24.0",
3536
"typeface-roboto": "^1.1.13",
3637
"uuid": "^9.0.1",
3738
"yup": "^1.4.0"

src/components/menus/content-contextual-menu.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ import { DialogsId } from '../../utils/UIconstants';
3030
import { ContingencyListType, FilterType } from '../../utils/elementType';
3131
import { ElementType, FilterCreationDialog, PARAM_LANGUAGE, useSnackMessage } from '@gridsuite/commons-ui';
3232
import CommonContextualMenu from './common-contextual-menu';
33-
import { useDeferredFetch, useMultipleDeferredFetch } from '../../utils/custom-hooks';
33+
import useDeferredFetch from '../../hooks/useDeferredFetch';
34+
import useMultipleDeferredFetch from '../../hooks/useMultipleDeferredFetch';
3435
import MoveDialog from '../dialogs/move-dialog';
3536
import { useDownloadUtils } from '../utils/caseUtils';
3637
import ExportCaseDialog from '../dialogs/export-case-dialog';

src/components/menus/directory-tree-contextual-menu.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import RenameDialog from '../dialogs/rename-dialog';
2323
import DeleteDialog from '../dialogs/delete-dialog';
2424
import { DialogsId } from '../../utils/UIconstants';
2525
import CommonContextualMenu from './common-contextual-menu';
26-
import { useDeferredFetch } from '../../utils/custom-hooks';
26+
import useDeferredFetch from '../../hooks/useDeferredFetch';
2727
import { ElementType, FilterCreationDialog, PARAM_LANGUAGE, useSnackMessage } from '@gridsuite/commons-ui';
2828
import ContingencyListCreationDialog from '../dialogs/contingency-list/creation/contingency-list-creation-dialog';
2929
import CreateCaseDialog from '../dialogs/create-case-dialog/create-case-dialog';

src/components/toolbars/content-toolbar.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
} from '@mui/icons-material';
1818
import DeleteDialog from '../dialogs/delete-dialog';
1919
import CommonToolbar from './common-toolbar';
20-
import { useMultipleDeferredFetch } from '../../utils/custom-hooks';
20+
import useMultipleDeferredFetch from '../../hooks/useMultipleDeferredFetch';
2121
import { ElementType, useSnackMessage } from '@gridsuite/commons-ui';
2222
import MoveDialog from '../dialogs/move-dialog';
2323
import { useDownloadUtils } from '../utils/caseUtils';

src/hooks/useDeferredFetch.ts

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/*
2+
* Copyright © 2024, RTE (http://www.rte-france.com)
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
*/
7+
8+
import { useCallback, useReducer } from 'react';
9+
import { FetchStatus, FetchStatusType } from '@gridsuite/commons-ui';
10+
11+
export enum ActionType {
12+
START = 'START',
13+
ERROR = 'ERROR',
14+
SUCCESS = 'SUCCESS',
15+
ADD_ERROR = 'ADD_ERROR', // Use by multipleDeferredFetch when one request respond with error
16+
ADD_SUCCESS = 'ADD_SUCCESS', // Use by multipleDeferredFetch when one request respond with success
17+
}
18+
19+
type FetchAction =
20+
| {
21+
type: ActionType.START;
22+
}
23+
| {
24+
type: ActionType.ERROR;
25+
payload: unknown;
26+
}
27+
| {
28+
type: ActionType.SUCCESS;
29+
payload: unknown;
30+
}
31+
| {
32+
type: ActionType.ADD_ERROR;
33+
}
34+
| {
35+
type: ActionType.ADD_SUCCESS;
36+
};
37+
38+
type FetchState = {
39+
status: FetchStatusType;
40+
data: unknown;
41+
errorMessage: unknown;
42+
};
43+
44+
const initialState: FetchState = {
45+
status: FetchStatus.IDLE,
46+
errorMessage: '',
47+
data: null,
48+
};
49+
50+
/**
51+
* This custom hook manage a fetch workflow and return a unique callback to defer process execution when needed.
52+
* It also returns a unique state which contains fetch status, results and error message if it failed.
53+
* @param {function} fetchFunction the fetch function to call
54+
* @param {Object} params Params of the fetch function. WARNING: Must respect order here
55+
* @param {function} onSuccess callback to call on request success
56+
* @param {function} errorToString callback to translate HTTPCode to string error messages
57+
* @param {function} onError callback to call if request failed
58+
* @param {boolean} hasResult Configure if fetchFunction return results or only HTTP request response
59+
* @returns {function} fetchCallback The callback to call to execute the request.
60+
* It accepts params as argument which must follow fetch function params.
61+
* @returns {state} state complete state of the request
62+
* {Enum} state.status Status of the request
63+
* {String} state.errorMessage error message of the request
64+
* {Object} state.data The JSON results of the request (see hasResult)
65+
*/
66+
export default function useDeferredFetch<TArgs extends any[]>(
67+
fetchFunction: (...args: TArgs) => Promise<void>,
68+
onSuccess: ((data: unknown | null, args: TArgs) => void) | undefined,
69+
errorToString: ((status: unknown) => string) | undefined = undefined,
70+
onError: ((errorMessage: unknown | null, paramsOnError: TArgs) => void) | undefined = undefined,
71+
hasResult: boolean = true
72+
): [(...args: TArgs) => void, FetchState] {
73+
const [state, dispatch] = useReducer((lastState: FetchState, action: FetchAction) => {
74+
switch (action.type) {
75+
case ActionType.START:
76+
return { ...initialState, status: FetchStatus.FETCHING };
77+
case ActionType.SUCCESS:
78+
return {
79+
...initialState,
80+
status: FetchStatus.FETCH_SUCCESS,
81+
data: action.payload,
82+
};
83+
case ActionType.ERROR:
84+
return {
85+
...initialState,
86+
status: FetchStatus.FETCH_ERROR,
87+
errorMessage: action.payload,
88+
};
89+
default:
90+
return lastState;
91+
}
92+
}, initialState);
93+
94+
const handleError = useCallback(
95+
(error: any, paramsOnError: TArgs) => {
96+
const defaultErrorMessage = error.message;
97+
let errorMessage = defaultErrorMessage;
98+
if (error && errorToString) {
99+
const providedErrorMessage = errorToString(error.status);
100+
if (providedErrorMessage && providedErrorMessage !== '') {
101+
errorMessage = providedErrorMessage;
102+
}
103+
}
104+
dispatch({
105+
type: ActionType.ERROR,
106+
payload: errorMessage,
107+
});
108+
if (onError) {
109+
onError(errorMessage, paramsOnError);
110+
}
111+
},
112+
[errorToString, onError]
113+
);
114+
115+
const fetchData = useCallback(
116+
async (...args: TArgs) => {
117+
dispatch({ type: ActionType.START });
118+
try {
119+
// Params resolution
120+
const response = await fetchFunction(...args);
121+
122+
if (hasResult) {
123+
dispatch({
124+
type: ActionType.SUCCESS,
125+
payload: response,
126+
});
127+
if (onSuccess) {
128+
onSuccess(response, args);
129+
}
130+
} else {
131+
dispatch({
132+
type: ActionType.SUCCESS,
133+
payload: null,
134+
});
135+
if (onSuccess) {
136+
onSuccess(null, args);
137+
}
138+
}
139+
} catch (error: any) {
140+
if (!error.status) {
141+
// an http error
142+
handleError(null, args);
143+
throw error;
144+
} else {
145+
handleError(error, args);
146+
}
147+
}
148+
},
149+
[fetchFunction, onSuccess, handleError, hasResult]
150+
);
151+
152+
const fetchCallback = useCallback(
153+
(...args: TArgs) => {
154+
fetchData(...args);
155+
},
156+
[fetchData]
157+
);
158+
159+
return [fetchCallback, state];
160+
}

0 commit comments

Comments
 (0)