fix: update instances with full data

This commit is contained in:
jialin
2025-01-21 10:31:40 +08:00
parent db733ac9ce
commit 6185485c68
4 changed files with 163 additions and 127 deletions
@@ -47,6 +47,7 @@ const TableRow: React.FC<
const childrenDataRef = useRef<any[]>([]); const childrenDataRef = useRef<any[]>([]);
childrenDataRef.current = childrenData; childrenDataRef.current = childrenData;
const axiosToken = useRef<any>(null); const axiosToken = useRef<any>(null);
const [updateChild, setUpdateChild] = useState(true);
const { updateChunkedList, cacheDataListRef } = useUpdateChunkedList({ const { updateChunkedList, cacheDataListRef } = useUpdateChunkedList({
dataList: childrenData, dataList: childrenData,
@@ -111,6 +112,9 @@ const TableRow: React.FC<
}; };
const filterUpdateChildrenHandler = () => { const filterUpdateChildrenHandler = () => {
if (!expanded) {
return;
}
const dataList = _.filter(tableContext.allChildren, (data: any) => { const dataList = _.filter(tableContext.allChildren, (data: any) => {
return _.get(data, [childParentKey]) === _.get(record, [rowKey]); return _.get(data, [childParentKey]) === _.get(record, [rowKey]);
}); });
@@ -161,6 +165,8 @@ const TableRow: React.FC<
pollTimer.current = setInterval(() => { pollTimer.current = setInterval(() => {
handlePolling(); handlePolling();
}, 1000); }, 1000);
} else {
handleLoadChildren();
} }
}; };
@@ -186,17 +192,13 @@ const TableRow: React.FC<
} }
}, [expandedRowKeys]); }, [expandedRowKeys]);
useEffect(() => {
if (expanded) {
handleLoadChildren();
}
}, [expanded]);
useEffect(() => { useEffect(() => {
const handleVisibilityChange = async () => { const handleVisibilityChange = async () => {
if (document.visibilityState === 'hidden') { if (document.visibilityState === 'hidden') {
cacheDataListRef.current = []; cacheDataListRef.current = [];
// setChildrenData([]); setUpdateChild(false);
} else {
setUpdateChild(true);
} }
}; };
@@ -208,15 +210,15 @@ const TableRow: React.FC<
}, []); }, []);
useEffect(() => { useEffect(() => {
if (!firstLoad && expanded) { if (updateChild) {
cacheDataListRef.current = childrenData; // for update watch data
filterUpdateChildrenHandler(); filterUpdateChildrenHandler();
} }
return () => { return () => {
chunkRequestRef.current?.current?.cancel?.(); chunkRequestRef.current?.current?.cancel?.();
cacheDataListRef.current = []; cacheDataListRef.current = [];
}; };
}, [firstLoad, expanded, tableContext.allChildren]); }, [updateChild, tableContext.allChildren]);
const renderRowPrefix = () => { const renderRowPrefix = () => {
if (expandable && rowSelection) { if (expandable && rowSelection) {
@@ -296,7 +298,8 @@ const TableRow: React.FC<
renderChildrenData() renderChildrenData()
) : ( ) : (
<Empty <Empty
image={Empty.PRESENTED_IMAGE_SIMPLE} image={loading ? null : Empty.PRESENTED_IMAGE_SIMPLE}
description={loading ? null : undefined}
style={{ style={{
marginBlock: 0, marginBlock: 0,
height: 54 height: 54
+65
View File
@@ -0,0 +1,65 @@
let bufferedData = '';
const findValidJSONStrings = (inputStr: string) => {
const validJSONStrings = [];
let startIndex = 0;
while (startIndex < inputStr.length) {
const openingBraceIndex = inputStr.indexOf('{', startIndex);
if (openingBraceIndex === -1) break; // No more opening braces
let closingBraceIndex = openingBraceIndex;
let braceCount = 0;
// find couple of braces
while (closingBraceIndex < inputStr.length) {
if (inputStr[closingBraceIndex] === '{') {
braceCount++;
} else if (inputStr[closingBraceIndex] === '}') {
braceCount--;
}
if (braceCount === 0) {
break;
}
closingBraceIndex++;
}
if (braceCount !== 0) {
// no matching closing brace
break;
}
const jsonString = inputStr.substring(
openingBraceIndex,
closingBraceIndex + 1
);
try {
const parsedData = JSON.parse(jsonString);
validJSONStrings.push(parsedData);
startIndex = closingBraceIndex + 1;
} catch (error) {
// mabye invalid JSON
break;
}
}
return {
parsedJSON: validJSONStrings,
remainingData: inputStr.slice(startIndex) // it is the not parsed data
};
};
// Web Worker
self.onmessage = function (event) {
bufferedData += event.data;
const { parsedJSON, remainingData } = findValidJSONStrings(bufferedData);
if (parsedJSON.length > 0) {
postMessage(parsedJSON);
}
// save the remaining data for the next message
bufferedData = remainingData;
};
+23 -90
View File
@@ -36,69 +36,6 @@ export const sliceJsonStr = (text: string) => {
return result; return result;
}; };
export const parseJsonStr = (list: string[]) => {
return _.map(list, (str: string) => {
return JSON.parse(str);
});
};
const findMatchingClosingBracket = (inputStr: string, startIndex: number) => {
let count = 0;
for (let i = startIndex; i < inputStr.length; i += 1) {
if (inputStr[i] === '{') {
count += 1;
} else if (inputStr[i] === '}') {
count -= 1;
}
if (count === 0) {
return i;
}
}
return -1;
};
const findValidJSONStrings = (inputStr: string) => {
const validJSONStrings: any[] = [];
let startIndex = 0;
while (startIndex < inputStr.length) {
const openingBraceIndex = inputStr.indexOf('{', startIndex);
if (openingBraceIndex === -1) {
break; // No more opening braces
}
const closingBraceIndex = findMatchingClosingBracket(
inputStr,
openingBraceIndex
);
if (closingBraceIndex === -1) {
// If no matching closing brace, set startIndex to next character
startIndex = openingBraceIndex + 1;
} else {
const jsonString = inputStr.substring(
openingBraceIndex,
closingBraceIndex + 1
);
try {
const data = JSON.parse(jsonString);
validJSONStrings.push(data);
} catch (error) {
// Ignore invalid JSON
}
startIndex = closingBraceIndex + 1;
}
}
return validJSONStrings;
};
const parseData = (data: string) => {
const res = findValidJSONStrings(data);
return res;
};
export const createAxiosToken = () => { export const createAxiosToken = () => {
const { CancelToken } = axios; const { CancelToken } = axios;
const source = CancelToken.source(); const source = CancelToken.source();
@@ -123,7 +60,7 @@ const useSetChunkRequest = () => {
const particalConfig = { params: {}, contentType: 'json' }; const particalConfig = { params: {}, contentType: 'json' };
const timer = useRef<any>(null); const timer = useRef<any>(null);
const loadedSize = useRef(0); const loadedSize = useRef(0);
const bufferedDataRef = useRef(''); const workerRef = useRef<any>(null);
const reset = () => { const reset = () => {
loaded.current = 0; loaded.current = 0;
@@ -172,6 +109,23 @@ const useSetChunkRequest = () => {
cancelWatchRequest(watchRequestList.length - 4 || 1); cancelWatchRequest(watchRequestList.length - 4 || 1);
} }
if (contentType === 'json') {
workerRef.current?.terminate();
workerRef.current = new Worker(
// @ts-ignore
new URL('./json-parser-worker.ts', import.meta.url)
);
workerRef.current.onmessage = function (event: any) {
const validJSON = event.data;
if (validJSON.length > 0) {
const result = resetResultSchema(validJSON);
handler(result);
}
};
}
try { try {
const { request: requestData } = await request(url, { const { request: requestData } = await request(url, {
params: { params: {
@@ -188,38 +142,17 @@ const useSetChunkRequest = () => {
loaded.current = e.loaded || 0; loaded.current = e.loaded || 0;
total.current = e.total || 0; total.current = e.total || 0;
let currentRes = sliceData(response, e.loaded, loadedSize);
let result: any[] = [];
let cres = currentRes;
if (contentType === 'json') { if (contentType === 'json') {
// Append the new data to the buffered data let currentRes = sliceData(response, e.loaded, loadedSize);
bufferedDataRef.current += currentRes; workerRef.current.postMessage(currentRes);
// Find valid JSON strings in the buffered data
let validJSON = findValidJSONStrings(bufferedDataRef.current);
if (validJSON.length > 0) {
result = resetResultSchema(validJSON);
// Calculate the position of the last complete JSON fragment, keeping the unfinished part
const lastValidJSON = validJSON[validJSON.length - 1];
const lastJSONIndex = bufferedDataRef.current.lastIndexOf(
JSON.stringify(lastValidJSON)
);
bufferedDataRef.current = bufferedDataRef.current.slice(
lastJSONIndex + JSON.stringify(lastValidJSON).length
);
}
handler(result);
} else { } else {
handler(currentRes); handler(response);
} }
console.log('chunkrequest===', { console.log('chunkrequest===', {
result, result: response,
url, url,
params, params
raw: cres
}); });
} }
}); });
+61 -26
View File
@@ -9,7 +9,12 @@ import {
import _ from 'lodash'; import _ from 'lodash';
import qs from 'query-string'; import qs from 'query-string';
import { memo, useCallback, useEffect, useRef, useState } from 'react'; import { memo, useCallback, useEffect, useRef, useState } from 'react';
import { MODELS_API, MODEL_INSTANCE_API, queryModelsList } from './apis'; import {
MODELS_API,
MODEL_INSTANCE_API,
queryModelsInstances,
queryModelsList
} from './apis';
import TableList from './components/table-list'; import TableList from './components/table-list';
import { ListItem } from './config/types'; import { ListItem } from './config/types';
@@ -81,39 +86,60 @@ const Models: React.FC = () => {
} }
}; };
const fetchData = useCallback(async () => { const getAllModelInstances = useCallback(async () => {
axiosToken?.cancel?.();
axiosToken = createAxiosToken();
setDataSource((pre) => {
pre.loading = true;
return { ...pre };
});
try { try {
instancesToken.current?.cancel?.();
instancesToken.current = createAxiosToken();
const params = { const params = {
..._.pickBy(queryParams, (val: any) => !!val) page: 1,
perPage: 100
}; };
const res: any = await queryModelsList(params, { const res: any = await queryModelsInstances(params, {
cancelToken: axiosToken.token token: instancesToken.current.token
});
setDataSource({
dataList: res.items || [],
loading: false,
loadend: true,
total: res.pagination.total,
deletedIds: []
}); });
cacheInsDataListRef.current = res.items || [];
setModelInstances(res.items || []);
} catch (error) { } catch (error) {
if (!isPageHidden.current) { // ignore
}
}, []);
const fetchData = useCallback(
async (loadingVal?: boolean) => {
axiosToken?.cancel?.();
axiosToken = createAxiosToken();
setDataSource((pre) => {
pre.loading = loadingVal ?? true;
return { ...pre };
});
try {
const params = {
..._.pickBy(queryParams, (val: any) => !!val)
};
const res: any = await queryModelsList(params, {
cancelToken: axiosToken.token
});
setDataSource({ setDataSource({
dataList: [], dataList: res.items || [],
loading: false, loading: false,
loadend: true, loadend: true,
total: dataSource.total, total: res.pagination.total,
deletedIds: [] deletedIds: []
}); });
} catch (error) {
if (!isPageHidden.current) {
setDataSource({
dataList: [],
loading: false,
loadend: true,
total: dataSource.total,
deletedIds: []
});
}
} }
} },
}, [queryParams]); [queryParams]
);
const handlePageChange = useCallback( const handlePageChange = useCallback(
(page: number, pageSize: number | undefined) => { (page: number, pageSize: number | undefined) => {
@@ -135,6 +161,7 @@ const Models: React.FC = () => {
const updateInstanceHandler = (list: any) => { const updateInstanceHandler = (list: any) => {
// filter the data // filter the data
_.each(list, (data: any) => { _.each(list, (data: any) => {
console.log('updateInstanceHandler====', data);
updateInstanceChunkedList(data); updateInstanceChunkedList(data);
}); });
}; };
@@ -166,7 +193,7 @@ const Models: React.FC = () => {
} catch (error) { } catch (error) {
// ignore // ignore
} }
}, []); }, [updateInstanceHandler]);
const handleOnViewLogs = useCallback(() => { const handleOnViewLogs = useCallback(() => {
isPageHidden.current = true; isPageHidden.current = true;
@@ -174,13 +201,15 @@ const Models: React.FC = () => {
cacheDataListRef.current = []; cacheDataListRef.current = [];
cacheInsDataListRef.current = []; cacheInsDataListRef.current = [];
chunkInstanceRequedtRef.current?.current?.cancel?.(); chunkInstanceRequedtRef.current?.current?.cancel?.();
instancesToken.current?.cancel?.();
}, []); }, []);
const handleOnCancelViewLogs = useCallback(async () => { const handleOnCancelViewLogs = useCallback(async () => {
isPageHidden.current = false; isPageHidden.current = false;
await getAllModelInstances();
await createModelsInstanceChunkRequest(); await createModelsInstanceChunkRequest();
await createModelsChunkRequest(); await createModelsChunkRequest();
fetchData(); fetchData(false);
}, [fetchData, createModelsChunkRequest, createModelsInstanceChunkRequest]); }, [fetchData, createModelsChunkRequest, createModelsInstanceChunkRequest]);
const handleSearch = useCallback(async () => { const handleSearch = useCallback(async () => {
@@ -232,19 +261,25 @@ const Models: React.FC = () => {
}; };
}, []); }, []);
useEffect(() => {
console.log('modelInstances====', modelInstances);
}, [modelInstances]);
useEffect(() => { useEffect(() => {
const handleVisibilityChange = async () => { const handleVisibilityChange = async () => {
if (document.visibilityState === 'visible') { if (document.visibilityState === 'visible') {
isPageHidden.current = false; isPageHidden.current = false;
await getAllModelInstances();
await createModelsInstanceChunkRequest(); await createModelsInstanceChunkRequest();
await createModelsChunkRequest(); await createModelsChunkRequest();
fetchData(); fetchData(false);
} else { } else {
isPageHidden.current = true; isPageHidden.current = true;
chunkRequedtRef.current?.current?.cancel?.(); chunkRequedtRef.current?.current?.cancel?.();
cacheDataListRef.current = []; cacheDataListRef.current = [];
cacheInsDataListRef.current = []; cacheInsDataListRef.current = [];
chunkInstanceRequedtRef.current?.current?.cancel?.(); chunkInstanceRequedtRef.current?.current?.cancel?.();
instancesToken.current?.cancel?.();
} }
}; };