Skip to content

Commit

Permalink
deduplicate some functions & types
Browse files Browse the repository at this point in the history
  • Loading branch information
Tristan-WorkGH committed Aug 12, 2024
1 parent af17f11 commit e35b633
Show file tree
Hide file tree
Showing 11 changed files with 410 additions and 346 deletions.
35 changes: 18 additions & 17 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"react-window": "^1.8.10",
"reconnecting-websocket": "^4.4.0",
"redux": "^5.0.1",
"type-fest": "^4.24.0",
"typeface-roboto": "^1.1.13",
"uuid": "^9.0.1",
"yup": "^1.4.0"
Expand Down
3 changes: 2 additions & 1 deletion src/components/menus/content-contextual-menu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ import { DialogsId } from '../../utils/UIconstants';
import { ContingencyListType, FilterType } from '../../utils/elementType';
import { ElementType, FilterCreationDialog, PARAM_LANGUAGE, useSnackMessage } from '@gridsuite/commons-ui';
import CommonContextualMenu from './common-contextual-menu';
import { useDeferredFetch, useMultipleDeferredFetch } from '../../utils/custom-hooks';
import useDeferredFetch from '../../hooks/useDeferredFetch';
import useMultipleDeferredFetch from '../../hooks/useMultipleDeferredFetch';
import MoveDialog from '../dialogs/move-dialog';
import { useDownloadUtils } from '../utils/caseUtils';
import ExportCaseDialog from '../dialogs/export-case-dialog';
Expand Down
2 changes: 1 addition & 1 deletion src/components/menus/directory-tree-contextual-menu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import RenameDialog from '../dialogs/rename-dialog';
import DeleteDialog from '../dialogs/delete-dialog';
import { DialogsId } from '../../utils/UIconstants';
import CommonContextualMenu from './common-contextual-menu';
import { useDeferredFetch } from '../../utils/custom-hooks';
import useDeferredFetch from '../../hooks/useDeferredFetch';
import { ElementType, FilterCreationDialog, PARAM_LANGUAGE, useSnackMessage } from '@gridsuite/commons-ui';
import ContingencyListCreationDialog from '../dialogs/contingency-list/creation/contingency-list-creation-dialog';
import CreateCaseDialog from '../dialogs/create-case-dialog/create-case-dialog';
Expand Down
2 changes: 1 addition & 1 deletion src/components/toolbars/content-toolbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
} from '@mui/icons-material';
import DeleteDialog from '../dialogs/delete-dialog';
import CommonToolbar from './common-toolbar';
import { useMultipleDeferredFetch } from '../../utils/custom-hooks';
import useMultipleDeferredFetch from '../../hooks/useMultipleDeferredFetch';
import { ElementType, useSnackMessage } from '@gridsuite/commons-ui';
import MoveDialog from '../dialogs/move-dialog';
import { useDownloadUtils } from '../utils/caseUtils';
Expand Down
160 changes: 160 additions & 0 deletions src/hooks/useDeferredFetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/*
* Copyright © 2024, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import { useCallback, useReducer } from 'react';
import { FetchStatus, FetchStatusType } from '@gridsuite/commons-ui';

export enum ActionType {
START = 'START',
ERROR = 'ERROR',
SUCCESS = 'SUCCESS',
ADD_ERROR = 'ADD_ERROR', // Use by multipleDeferredFetch when one request respond with error
ADD_SUCCESS = 'ADD_SUCCESS', // Use by multipleDeferredFetch when one request respond with success
}

type FetchAction =
| {
type: ActionType.START;
}
| {
type: ActionType.ERROR;
payload: unknown;
}
| {
type: ActionType.SUCCESS;
payload: unknown;
}
| {
type: ActionType.ADD_ERROR;
}
| {
type: ActionType.ADD_SUCCESS;
};

type FetchState = {
status: FetchStatusType;
data: unknown;
errorMessage: unknown;
};

const initialState: FetchState = {
status: FetchStatus.IDLE,
errorMessage: '',
data: null,
};

/**
* This custom hook manage a fetch workflow and return a unique callback to defer process execution when needed.
* It also returns a unique state which contains fetch status, results and error message if it failed.
* @param {function} fetchFunction the fetch function to call
* @param {Object} params Params of the fetch function. WARNING: Must respect order here
* @param {function} onSuccess callback to call on request success
* @param {function} errorToString callback to translate HTTPCode to string error messages
* @param {function} onError callback to call if request failed
* @param {boolean} hasResult Configure if fetchFunction return results or only HTTP request response
* @returns {function} fetchCallback The callback to call to execute the request.
* It accepts params as argument which must follow fetch function params.
* @returns {state} state complete state of the request
* {Enum} state.status Status of the request
* {String} state.errorMessage error message of the request
* {Object} state.data The JSON results of the request (see hasResult)
*/
export default function useDeferredFetch<TArgs extends any[]>(
fetchFunction: (...args: TArgs) => Promise<void>,
onSuccess: ((data: unknown | null, args: TArgs) => void) | undefined,
errorToString: ((status: unknown) => string) | undefined = undefined,
onError: ((errorMessage: unknown | null, paramsOnError: TArgs) => void) | undefined = undefined,
hasResult: boolean = true
): [(...args: TArgs) => void, FetchState] {
const [state, dispatch] = useReducer((lastState: FetchState, action: FetchAction) => {
switch (action.type) {
case ActionType.START:
return { ...initialState, status: FetchStatus.FETCHING };
case ActionType.SUCCESS:
return {
...initialState,
status: FetchStatus.FETCH_SUCCESS,
data: action.payload,
};
case ActionType.ERROR:
return {
...initialState,
status: FetchStatus.FETCH_ERROR,
errorMessage: action.payload,
};
default:
return lastState;
}
}, initialState);

const handleError = useCallback(
(error: any, paramsOnError: TArgs) => {
const defaultErrorMessage = error.message;
let errorMessage = defaultErrorMessage;
if (error && errorToString) {
const providedErrorMessage = errorToString(error.status);
if (providedErrorMessage && providedErrorMessage !== '') {
errorMessage = providedErrorMessage;
}
}
dispatch({
type: ActionType.ERROR,
payload: errorMessage,
});
if (onError) {
onError(errorMessage, paramsOnError);
}
},
[errorToString, onError]
);

const fetchData = useCallback(
async (...args: TArgs) => {
dispatch({ type: ActionType.START });
try {
// Params resolution
const response = await fetchFunction(...args);

if (hasResult) {
dispatch({
type: ActionType.SUCCESS,
payload: response,
});
if (onSuccess) {
onSuccess(response, args);
}
} else {
dispatch({
type: ActionType.SUCCESS,
payload: null,
});
if (onSuccess) {
onSuccess(null, args);
}
}
} catch (error: any) {
if (!error.status) {
// an http error
handleError(null, args);
throw error;
} else {
handleError(error, args);
}
}
},
[fetchFunction, onSuccess, handleError, hasResult]
);

const fetchCallback = useCallback(
(...args: TArgs) => {
fetchData(...args);
},
[fetchData]
);

return [fetchCallback, state];
}
Loading

0 comments on commit e35b633

Please sign in to comment.