WEBAPP: P8PDataGrid - управление состоянием хука переписано на редьюсер для обеспечения атомарности обновления связанных состояний
This commit is contained in:
parent
3e6afa284d
commit
63751700b4
@ -7,9 +7,10 @@
|
||||
//Подключение библиотек
|
||||
//---------------------
|
||||
|
||||
import { useState, useCallback, useEffect, useContext, useRef, useMemo } from "react"; //Классы React
|
||||
import { useReducer, useCallback, useEffect, useContext, useRef, useMemo } from "react"; //Классы React
|
||||
import { BackEndCtx } from "../context/backend"; //Контекст взаимодействия с сервером
|
||||
import { object2Base64XML } from "../core/utils"; //Вспомогательные функции
|
||||
import { DG_AT, INITIAL_STATE, dataGridReducer } from "./p8p_data_grid_reducer"; //Редьюсер состояния
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
@ -38,38 +39,39 @@ const useP8PDataGrid = ({
|
||||
initFilters = [],
|
||||
initOrders = [],
|
||||
storedArgs = {},
|
||||
resetPageNumberOnStoredArgsChange = true,
|
||||
executeStoredArgs = {},
|
||||
resetPageNumberOnExecuteStoredArgsChange = true,
|
||||
allowDataLoad = () => true
|
||||
}) => {
|
||||
//Собственное состояние - таблица данных
|
||||
const [dataGrid, setDataGrid] = useState({
|
||||
columnsDef: [],
|
||||
groups: [],
|
||||
rows: [],
|
||||
filters: Array.isArray(initFilters) ? [...initFilters] : [],
|
||||
orders: Array.isArray(initOrders) ? [...initOrders] : [],
|
||||
pageNumber: 1,
|
||||
pagesAlign: null,
|
||||
pagesPosition: null,
|
||||
pagesCount: 0,
|
||||
fixedColumns: 0,
|
||||
fixedHeader: false,
|
||||
morePages: true
|
||||
});
|
||||
//Подключим редьюсер состояния
|
||||
const [state, dispatch] = useReducer(dataGridReducer, INITIAL_STATE({ initFilters, initOrders }));
|
||||
|
||||
//Собственное состояние - признак загрузки данных
|
||||
const [isDataLoaded, setIsDataLoaded] = useState(false);
|
||||
//Установка даных таблицы
|
||||
const setDataGrid = (dataGridData, pageSize, isError) => dispatch({ type: DG_AT.SET_DATA_GRID, payload: { dataGridData, pageSize, isError } });
|
||||
|
||||
//Собственное состояние - флаг загрузки
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
//Установка фильтра таблицы
|
||||
const setDataGridFilter = filters => dispatch({ type: DG_AT.SET_DATA_GRID_FILTER, payload: filters });
|
||||
|
||||
//Собственное состояние - необходимость обновления данных
|
||||
const [reload, setReload] = useState(true);
|
||||
//Установка сортировок таблицы
|
||||
const setDataGridOrder = orders => dispatch({ type: DG_AT.SET_DATA_GRID_ORDER, payload: orders });
|
||||
|
||||
//Собственное состояние - дополнительные параметры процедуры
|
||||
//Установка страницы таблицы
|
||||
const setDataGridPageNumber = pageNumber => dispatch({ type: DG_AT.SET_DATA_GRID_PAGE_NUMBER, payload: pageNumber });
|
||||
|
||||
//Установка флага загруженности данных
|
||||
const setIsDataLoaded = isDataLoaded => dispatch({ type: DG_AT.SET_IS_DATA_LOADED, payload: isDataLoaded });
|
||||
|
||||
//Установка флага активности процесса загрузки данных
|
||||
const setIsLoading = isLoading => dispatch({ type: DG_AT.SET_IS_LOADING, payload: isLoading });
|
||||
|
||||
//Установка флага необходимости обновления данных
|
||||
const setReload = (reload, resetPageNumber = false) => dispatch({ type: DG_AT.SET_RELOAD, payload: { reload, resetPageNumber } });
|
||||
|
||||
//Ссылка на актуальные параметры хранимой процедуры
|
||||
const refStoredArgs = useRef(storedArgs);
|
||||
|
||||
//Собственное состояние - дополнительные параметры вызова процедуры
|
||||
//Ссылка на актуальные параметры исполнения хранимой процедуры
|
||||
const refExecuteStoredArgs = useRef(executeStoredArgs);
|
||||
|
||||
//Признак допустимости обновления данных
|
||||
@ -83,52 +85,40 @@ const useP8PDataGrid = ({
|
||||
//Загрузка данных таблицы с сервера
|
||||
const loadData = useCallback(async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
//Начинаем загрузку
|
||||
setIsLoading(true);
|
||||
const data = await executeStored({
|
||||
stored,
|
||||
args: {
|
||||
CFILTERS: { VALUE: object2Base64XML(dataGrid.filters, { arrayNodeName: filtersNodeName }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
|
||||
CORDERS: { VALUE: object2Base64XML(dataGrid.orders, { arrayNodeName: ordersNodeName }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
|
||||
NPAGE_NUMBER: dataGrid.pageNumber,
|
||||
CFILTERS: {
|
||||
VALUE: object2Base64XML(state.dataGrid.filters, { arrayNodeName: filtersNodeName }),
|
||||
SDATA_TYPE: SERV_DATA_TYPE_CLOB
|
||||
},
|
||||
CORDERS: { VALUE: object2Base64XML(state.dataGrid.orders, { arrayNodeName: ordersNodeName }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
|
||||
NPAGE_NUMBER: state.dataGrid.pageNumber,
|
||||
NPAGE_SIZE: pageSize,
|
||||
NINCLUDE_DEF: reloadDef ? 1 : dataGrid.dataLoaded ? 0 : 1,
|
||||
NINCLUDE_DEF: reloadDef ? 1 : state.isDataLoaded ? 0 : 1,
|
||||
...refStoredArgs.current
|
||||
},
|
||||
respArg,
|
||||
...refExecuteStoredArgs.current
|
||||
});
|
||||
setDataGrid(pv => ({
|
||||
...pv,
|
||||
...data[contentNodeName],
|
||||
columnsDef: data[contentNodeName].columnsDef ? [...data[contentNodeName].columnsDef] : pv.columnsDef || [],
|
||||
rows:
|
||||
data[contentNodeName].pagesCount > 0 || pv.pageNumber == 1
|
||||
? [...(data[contentNodeName].rows || [])]
|
||||
: [...(pv.rows || []), ...(data[contentNodeName].rows || [])],
|
||||
groups: data[contentNodeName].groups
|
||||
? data[contentNodeName].pagesCount > 0 || pv.pageNumber == 1
|
||||
? [...(data[contentNodeName].groups || [])]
|
||||
: [...(pv.groups || []), ...data[contentNodeName].groups.filter(g => !pv.groups.find(pg => pg.name == g.name))]
|
||||
: [...(pv.groups || [])],
|
||||
morePages: data[contentNodeName].morePages && (data[contentNodeName].rows || []).length >= pageSize
|
||||
}));
|
||||
//Устанавливаем признак загрузки данных с учетом возможных ошибок
|
||||
setIsDataLoaded(!isRespErr(data));
|
||||
//Устанавливаем полученные данные и признак загрузки данных с учетом возможных ошибок
|
||||
setDataGrid(data[contentNodeName], pageSize, isRespErr(data));
|
||||
} catch (e) {
|
||||
//Если произошла ошибка - данные не загружены
|
||||
setIsDataLoaded(false);
|
||||
} finally {
|
||||
//Сбрасываем признаки загрузки и перезагрузки данных
|
||||
setLoading(false);
|
||||
setReload(false);
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [
|
||||
SERV_DATA_TYPE_CLOB,
|
||||
contentNodeName,
|
||||
dataGrid.dataLoaded,
|
||||
dataGrid.filters,
|
||||
dataGrid.orders,
|
||||
dataGrid.pageNumber,
|
||||
state.isDataLoaded,
|
||||
state.dataGrid.filters,
|
||||
state.dataGrid.orders,
|
||||
state.dataGrid.pageNumber,
|
||||
executeStored,
|
||||
filtersNodeName,
|
||||
isRespErr,
|
||||
@ -140,48 +130,30 @@ const useP8PDataGrid = ({
|
||||
]);
|
||||
|
||||
//При изменении состояния фильтра
|
||||
const handleFilterChanged = useCallback(({ filters }) => {
|
||||
setDataGrid(pv => ({ ...pv, filters: [...filters], pageNumber: 1 }));
|
||||
setReload(true);
|
||||
}, []);
|
||||
const handleFilterChanged = useCallback(({ filters }) => setDataGridFilter(filters), []);
|
||||
|
||||
//При изменении состояния сортировки
|
||||
const handleOrderChanged = useCallback(({ orders }) => {
|
||||
setDataGrid(pv => ({ ...pv, orders: [...orders], pageNumber: 1 }));
|
||||
setReload(true);
|
||||
}, []);
|
||||
const handleOrderChanged = useCallback(({ orders }) => setDataGridOrder(orders), []);
|
||||
|
||||
//При изменении количества отображаемых страниц
|
||||
const handlePagesCountChanged = useCallback(() => {
|
||||
setDataGrid(pv => ({ ...pv, pageNumber: pv.pageNumber + 1 }));
|
||||
setReload(true);
|
||||
}, []);
|
||||
const handlePagesCountChanged = useCallback(() => setDataGridPageNumber(state.dataGrid.pageNumber + 1), [state.dataGrid.pageNumber]);
|
||||
|
||||
//При изменении страницы отображения
|
||||
const handlePageChange = useCallback(({ page }) => {
|
||||
setDataGrid(pv => ({ ...pv, pageNumber: page }));
|
||||
setReload(true);
|
||||
}, []);
|
||||
const handlePageChange = useCallback(({ page }) => setDataGridPageNumber(page), []);
|
||||
|
||||
//При необходимости обновления таблицы
|
||||
const doReload = useCallback(
|
||||
({ returnOnFirstPage = false }) => {
|
||||
//Если это не страничный вывод или установлен признак возврата на первую страницу
|
||||
if (dataGrid.pagesCount <= 0 || returnOnFirstPage) {
|
||||
setDataGrid(pv => ({ ...pv, pageNumber: 1 }));
|
||||
}
|
||||
setReload(true);
|
||||
},
|
||||
[dataGrid.pagesCount]
|
||||
({ returnOnFirstPage = false }) => setReload(true, state.dataGrid.pagesCount <= 0 || returnOnFirstPage),
|
||||
[state.dataGrid.pagesCount]
|
||||
);
|
||||
|
||||
//Проверка изменений параметров
|
||||
const isArgsChanged = useCallback(
|
||||
(currentArgs, args) => {
|
||||
//Если дополнительные параметры изменились (и сейчас не происходит загрузка данных с сервера)
|
||||
return !isLoading && JSON.stringify(currentArgs) != JSON.stringify(args);
|
||||
return !state.isLoading && JSON.stringify(currentArgs) != JSON.stringify(args);
|
||||
},
|
||||
[isLoading]
|
||||
[state.isLoading]
|
||||
);
|
||||
|
||||
//При изменение дополнительных параметров процедуры
|
||||
@ -191,9 +163,9 @@ const useP8PDataGrid = ({
|
||||
//Устанавливаем новые дополнительные параметры
|
||||
refStoredArgs.current = storedArgs;
|
||||
//При изменении дополнительных параметров необходимо перезагрузить данные
|
||||
setReload(true);
|
||||
setReload(true, resetPageNumberOnStoredArgsChange);
|
||||
}
|
||||
}, [storedArgs, isArgsChanged]);
|
||||
}, [storedArgs, resetPageNumberOnStoredArgsChange, isArgsChanged]);
|
||||
|
||||
//При изменение дополнительных параметров вызова процедуры
|
||||
useEffect(() => {
|
||||
@ -202,19 +174,28 @@ const useP8PDataGrid = ({
|
||||
//Устанавливаем новые дополнительные параметры
|
||||
refExecuteStoredArgs.current = executeStoredArgs;
|
||||
//При изменении дополнительных параметров необходимо перезагрузить данные
|
||||
setReload(true);
|
||||
setReload(true, resetPageNumberOnExecuteStoredArgsChange);
|
||||
}
|
||||
}, [executeStoredArgs, isArgsChanged]);
|
||||
}, [executeStoredArgs, resetPageNumberOnExecuteStoredArgsChange, isArgsChanged]);
|
||||
|
||||
//При необходимости обновить данные таблицы
|
||||
useEffect(() => {
|
||||
if (isAllowDataLoad && reload) {
|
||||
if (isAllowDataLoad && state.reload) {
|
||||
loadData();
|
||||
}
|
||||
}, [isAllowDataLoad, reload, loadData]);
|
||||
}, [isAllowDataLoad, state.reload, loadData]);
|
||||
|
||||
//Возвращаем данные таблицы
|
||||
return { dataGrid, isDataLoaded, isLoading, handleFilterChanged, handleOrderChanged, handlePagesCountChanged, handlePageChange, doReload };
|
||||
return {
|
||||
dataGrid: state.dataGrid,
|
||||
isDataLoaded: state.isDataLoaded,
|
||||
isLoading: state.isLoading,
|
||||
handleFilterChanged,
|
||||
handleOrderChanged,
|
||||
handlePagesCountChanged,
|
||||
handlePageChange,
|
||||
doReload
|
||||
};
|
||||
};
|
||||
|
||||
//----------------
|
||||
|
||||
112
app/components/p8p_data_grid_reducer.js
Normal file
112
app/components/p8p_data_grid_reducer.js
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
Парус 8 - Панели мониторинга
|
||||
Таблица данных - редьюсер состояния
|
||||
*/
|
||||
|
||||
//---------
|
||||
//Константы
|
||||
//---------
|
||||
|
||||
//Типы действий
|
||||
const DG_AT = {
|
||||
SET_DATA_GRID: "SET_DATA_GRID", //Установка даных таблицы
|
||||
SET_DATA_GRID_FILTER: "SET_DATA_GRID_FILTER", //Установка фильтра таблицы
|
||||
SET_DATA_GRID_ORDER: "SET_DATA_GRID_ORDER", //Установка сортировок таблицы
|
||||
SET_DATA_GRID_PAGE_NUMBER: "SET_DATA_GRID_PAGE_NUMBER", //Установка страницы таблицы
|
||||
SET_IS_DATA_LOADED: "SET_IS_DATA_LOADED", //Установка флага загруженности данных
|
||||
SET_IS_LOADING: "SET_IS_LOADING", //Установка флага активности процесса загрузки данных
|
||||
SET_RELOAD: "SET_RELOAD" //Установка флага необходимости обновления данных
|
||||
};
|
||||
|
||||
//Состояние приложения по умолчанию
|
||||
const INITIAL_STATE = ({ initFilters, initOrders }) => ({
|
||||
dataGrid: {
|
||||
columnsDef: [],
|
||||
groups: [],
|
||||
rows: [],
|
||||
filters: Array.isArray(initFilters) ? [...initFilters] : [],
|
||||
orders: Array.isArray(initOrders) ? [...initOrders] : [],
|
||||
pageNumber: 1,
|
||||
pagesAlign: null,
|
||||
pagesPosition: null,
|
||||
pagesCount: 0,
|
||||
fixedColumns: 0,
|
||||
fixedHeader: false,
|
||||
morePages: true
|
||||
},
|
||||
isDataLoaded: false,
|
||||
isLoading: false,
|
||||
reload: true
|
||||
});
|
||||
|
||||
//-----------
|
||||
//Тело модуля
|
||||
//-----------
|
||||
|
||||
//Обработчики действий
|
||||
const handlers = {
|
||||
//Установка даных таблицы
|
||||
[DG_AT.SET_DATA_GRID]: (state, { payload }) => {
|
||||
const { dataGridData, pageSize, isError } = payload;
|
||||
return {
|
||||
...state,
|
||||
dataGrid: {
|
||||
...state.dataGrid,
|
||||
...dataGridData,
|
||||
columnsDef: dataGridData.columnsDef ? [...dataGridData.columnsDef] : state.dataGrid.columnsDef || [],
|
||||
rows:
|
||||
dataGridData.pagesCount > 0 || state.dataGrid.pageNumber == 1
|
||||
? [...(dataGridData.rows || [])]
|
||||
: [...(state.dataGrid.rows || []), ...(dataGridData.rows || [])],
|
||||
groups: dataGridData.groups
|
||||
? dataGridData.pagesCount > 0 || state.dataGrid.pageNumber == 1
|
||||
? [...(dataGridData.groups || [])]
|
||||
: [...(state.dataGrid.groups || []), ...dataGridData.groups.filter(g => !state.dataGrid.groups.find(pg => pg.name == g.name))]
|
||||
: [...(state.dataGrid.groups || [])],
|
||||
morePages: dataGridData.morePages && (dataGridData.rows || []).length >= pageSize
|
||||
},
|
||||
isDataLoaded: isError === true ? false : true
|
||||
};
|
||||
},
|
||||
//Установка фильтра таблицы
|
||||
[DG_AT.SET_DATA_GRID_FILTER]: (state, { payload }) => ({
|
||||
...state,
|
||||
dataGrid: { ...state.dataGrid, filters: [...payload], pageNumber: 1 },
|
||||
reload: true
|
||||
}),
|
||||
//Установка сортировок таблицы
|
||||
[DG_AT.SET_DATA_GRID_ORDER]: (state, { payload }) => ({
|
||||
...state,
|
||||
dataGrid: { ...state.dataGrid, orders: [...payload], pageNumber: 1 },
|
||||
reload: true
|
||||
}),
|
||||
//Установка страницы таблицы
|
||||
[DG_AT.SET_DATA_GRID_PAGE_NUMBER]: (state, { payload }) => ({ ...state, dataGrid: { ...state.dataGrid, pageNumber: payload }, reload: true }),
|
||||
//Установка флага загруженности данных
|
||||
[DG_AT.SET_IS_DATA_LOADED]: (state, { payload }) => ({ ...state, isDataLoaded: payload }),
|
||||
//Установка флага активности процесса загрузки данных
|
||||
[DG_AT.SET_IS_LOADING]: (state, { payload }) => ({ ...state, isLoading: payload, reload: payload === false ? false : state.reload }),
|
||||
//Установка флага необходимости обновления данных
|
||||
[DG_AT.SET_RELOAD]: (state, { payload }) => ({
|
||||
...state,
|
||||
reload: payload.reload,
|
||||
...(payload.resetPageNumber ? { dataGrid: { ...state.dataGrid, pageNumber: 1 } } : {})
|
||||
}),
|
||||
//Обработчик по умолчанию
|
||||
DEFAULT: state => state
|
||||
};
|
||||
|
||||
//----------------
|
||||
//Интерфейс модуля
|
||||
//----------------
|
||||
|
||||
//Константы
|
||||
export { DG_AT, INITIAL_STATE };
|
||||
|
||||
//Редьюсер состояния
|
||||
export const dataGridReducer = (state, action) => {
|
||||
//Подберём обработчик
|
||||
const handle = handlers[action.type] || handlers.DEFAULT;
|
||||
//Исполним его
|
||||
return handle(state, action);
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user