Skip to content

Commit 6a7bc8f

Browse files
authored
Merge pull request #228 from projectdiscovery/performance-issue-fix
Performance issue fix
2 parents c4c405c + 8367296 commit 6a7bc8f

6 files changed

Lines changed: 254 additions & 107 deletions

File tree

src/app/components/requestDetailsWrapper.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
'use client';
22

3-
import React, { Suspense } from 'react';
3+
import React, { Suspense, memo } from 'react';
44
import { ErrorBoundary } from 'react-error-boundary';
55
import DetailedRequest from '@/components/detailedRequest';
66
import { Data } from '@/lib/types/data';
7-
import { Protocol } from '@/lib/types/protocol';
87
import { View } from '@/lib/types/view';
98
import { IssuesListFallback, IssuesListErrorFallback } from '@/helpers/fallback-loaders';
109

@@ -13,9 +12,8 @@ interface RequestDetailsWrapperP {
1312
selectedInteractionData: Data;
1413
}
1514

16-
const RequestDetailsWrapper = (props: RequestDetailsWrapperP) => {
15+
const RequestDetailsWrapper = memo((props: RequestDetailsWrapperP) => {
1716
const { selectedInteractionData, view } = props;
18-
console.log(selectedInteractionData.protocol);
1917

2018
return (
2119
<div
@@ -63,7 +61,7 @@ const RequestDetailsWrapper = (props: RequestDetailsWrapperP) => {
6361
</ErrorBoundary>
6462
</div>
6563
);
66-
};
64+
});
6765

6866
export default RequestDetailsWrapper;
6967

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client';
22

3-
import React, { Suspense } from 'react';
3+
import React, { Suspense, useMemo, memo } from 'react';
44
import { ErrorBoundary } from 'react-error-boundary';
55
import RequestsTable from '@/components/requestsTable';
66
import { Data } from '@/lib/types/data';
@@ -17,29 +17,33 @@ interface RequestsTableWrapperP {
1717
filter: Filter;
1818
}
1919

20-
const RequestsTableWrapper = ({
20+
const RequestsTableWrapper = memo(({
2121
data,
2222
handleRowClick,
2323
selectedInteraction,
2424
filter,
25-
}: RequestsTableWrapperP) => (
26-
<div className="requests_table_container">
27-
<ErrorBoundary
28-
FallbackComponent={({ resetErrorBoundary }) => (
29-
<RepoSidebarListErrorFallback retry={resetErrorBoundary} />
30-
)}
31-
>
32-
<Suspense fallback={<RepoSidebarListFallback />}>
33-
<RequestsTable
34-
data={[...data].reverse()}
35-
handleRowClick={handleRowClick}
36-
selectedInteraction={selectedInteraction}
37-
filter={filter}
38-
/>
39-
</Suspense>
40-
</ErrorBoundary>
41-
</div>
42-
);
25+
}: RequestsTableWrapperP) => {
26+
const reversedData = useMemo(() => [...data].reverse(), [data]);
27+
28+
return (
29+
<div className="requests_table_container">
30+
<ErrorBoundary
31+
FallbackComponent={({ resetErrorBoundary }) => (
32+
<RepoSidebarListErrorFallback retry={resetErrorBoundary} />
33+
)}
34+
>
35+
<Suspense fallback={<RepoSidebarListFallback />}>
36+
<RequestsTable
37+
data={reversedData}
38+
handleRowClick={handleRowClick}
39+
selectedInteraction={selectedInteraction}
40+
filter={filter}
41+
/>
42+
</Suspense>
43+
</ErrorBoundary>
44+
</div>
45+
);
46+
});
4347

4448
export default RequestsTableWrapper;
4549

src/app/page.tsx

Lines changed: 90 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client';
22

3-
import React, { useEffect, useState, useCallback } from 'react';
3+
import React, { useEffect, useState, useCallback, useMemo, useRef } from 'react';
44
import { format } from 'date-fns';
55
import { ThemeProvider } from 'styled-components';
66

@@ -23,7 +23,6 @@ import {
2323
processData,
2424
handleResponseExport,
2525
copyDataToClipboard,
26-
clearIntervals,
2726
register,
2827
} from '@/lib';
2928
import { notifyTelegram, notifySlack, notifyDiscord } from '@/lib/notify';
@@ -32,7 +31,7 @@ import { StoredData } from '@/lib/types/storedData';
3231
import { Tab } from '@/lib/types/tab';
3332
import { View } from '@/lib/types/view';
3433
import { ThemeName, getTheme } from '@/theme';
35-
import { writeStoredData, getStoredData, defaultStoredData } from '@/lib/localStorage';
34+
import { writeStoredData, getStoredData, defaultStoredData, flushStoredData } from '@/lib/localStorage';
3635
import RequestDetailsWrapper from './components/requestDetailsWrapper';
3736
import RequestsTableWrapper from './components/requestsTableWrapper';
3837
import './styles.scss';
@@ -50,11 +49,15 @@ const HomePage = () => {
5049
const [storedData, setStoredData] = useState<StoredData>(defaultStoredData);
5150
const [isCustomHostDialogVisible, setIsCustomHostDialogVisible] = useState(false);
5251
const [isClient, setIsClient] = useState(false);
52+
const hasLoadedInitialData = useRef(false);
53+
const filteredDataRef = useRef<Array<Data>>([]);
5354

5455
// Initialize stored data on client side
5556
useEffect(() => {
5657
setIsClient(true);
57-
setStoredData(getStoredData());
58+
const loadedData = getStoredData();
59+
setStoredData(loadedData);
60+
hasLoadedInitialData.current = true;
5861
}, []);
5962

6063
const handleResetPopupDialogVisibility = useCallback(() => {
@@ -123,11 +126,13 @@ const HomePage = () => {
123126
});
124127
};
125128

126-
const handleRowClick = (id: string) => {
129+
const handleRowClick = useCallback((id: string) => {
127130
setSelectedInteraction(id);
128-
const reqDetails = filteredData && filteredData[filteredData.findIndex((item) => item.id === id)];
129-
setSelectedInteractionData(reqDetails);
130-
};
131+
const reqDetails = filteredDataRef.current.find((item) => item.id === id);
132+
if (reqDetails) {
133+
setSelectedInteractionData(reqDetails);
134+
}
135+
}, []);
131136

132137
const handleDeleteTab = (tab: Tab) => {
133138
const { tabs } = storedData;
@@ -179,6 +184,7 @@ const HomePage = () => {
179184
...storedData,
180185
data: tempData,
181186
});
187+
filteredDataRef.current = [];
182188
setFilteredData([]);
183189
};
184190

@@ -199,7 +205,8 @@ const HomePage = () => {
199205
);
200206

201207
setIsRegistered(true);
202-
if (pollData?.data?.length !== 0 && !pollData.error) {
208+
209+
if (pollData?.data?.length > 0 && !pollData.error) {
203210
if (pollData.aes_key) {
204211
decryptedAESKey = decryptAESKey(privateKey, pollData.aes_key);
205212
}
@@ -236,9 +243,9 @@ const HomePage = () => {
236243
});
237244

238245
const newData = combinedData
239-
.filter((item) => item['unique-id'] === dataFromLocalStorage.selectedTab['unique-id'])
240-
.map((item) => item);
241-
setFilteredData([...newData]);
246+
.filter((item) => item['unique-id'] === dataFromLocalStorage.selectedTab['unique-id']);
247+
filteredDataRef.current = newData;
248+
setFilteredData(newData);
242249
}
243250
} catch (error) {
244251
console.error(error);
@@ -248,7 +255,9 @@ const HomePage = () => {
248255
}, [handleResetPopupDialogVisibility, handleCustomHostDialogVisibility]);
249256

250257
useEffect(() => {
251-
writeStoredData(storedData);
258+
if (hasLoadedInitialData.current) {
259+
writeStoredData(storedData);
260+
}
252261
}, [storedData]);
253262

254263
useEffect(() => {
@@ -261,18 +270,16 @@ const HomePage = () => {
261270
window.addEventListener('storage', handleStorageChange);
262271
setIsRegistered(true);
263272

273+
let registrationIntervalId: number | undefined;
274+
264275
if (storedData.correlationId === '') {
265276
setLoaderAnimationMode('loading');
266277
setIsRegistered(false);
267-
setTimeout(() => {
278+
const timeoutId = setTimeout(() => {
268279
register(storedData.host, storedData.token, false, false)
269280
.then((data) => {
270281
setStoredData(data);
271-
const intervalId = window.setInterval(() => {
272-
processPolledData();
273-
}, 4000);
274282
setIsRegistered(true);
275-
return intervalId;
276283
})
277284
.catch(() => {
278285
localStorage.clear();
@@ -281,42 +288,96 @@ const HomePage = () => {
281288
setIsRegistered(false);
282289
});
283290
}, 1500);
291+
292+
return () => {
293+
clearTimeout(timeoutId);
294+
window.removeEventListener('storage', handleStorageChange);
295+
if (registrationIntervalId) {
296+
window.clearInterval(registrationIntervalId);
297+
}
298+
};
284299
}
285300

286301
return () => {
287302
window.removeEventListener('storage', handleStorageChange);
288303
};
289304
}, [isClient]);
290305

306+
const pollingIntervalRef = useRef<number | undefined>(undefined);
307+
const isPageVisibleRef = useRef(true);
308+
309+
useEffect(() => {
310+
const handleVisibilityChange = () => {
311+
isPageVisibleRef.current = !document.hidden;
312+
313+
if (document.hidden) {
314+
if (pollingIntervalRef.current) {
315+
window.clearInterval(pollingIntervalRef.current);
316+
pollingIntervalRef.current = undefined;
317+
}
318+
flushStoredData();
319+
} else if (isClient && storedData.tabs.length > 0) {
320+
if (!pollingIntervalRef.current) {
321+
processPolledData(); // Immediate poll on return
322+
pollingIntervalRef.current = window.setInterval(() => {
323+
processPolledData();
324+
}, 4000);
325+
}
326+
}
327+
};
328+
329+
document.addEventListener('visibilitychange', handleVisibilityChange);
330+
331+
const handleBeforeUnload = () => {
332+
flushStoredData();
333+
};
334+
window.addEventListener('beforeunload', handleBeforeUnload);
335+
336+
return () => {
337+
document.removeEventListener('visibilitychange', handleVisibilityChange);
338+
window.removeEventListener('beforeunload', handleBeforeUnload);
339+
};
340+
}, [isClient, storedData.tabs.length, processPolledData]);
341+
291342
useEffect(() => {
292343
if (!isClient || storedData.tabs.length === 0) return;
293344

294-
clearIntervals();
295-
const intervalId = window.setInterval(() => {
296-
processPolledData();
297-
}, 4000);
345+
if (pollingIntervalRef.current) {
346+
window.clearInterval(pollingIntervalRef.current);
347+
}
348+
349+
if (isPageVisibleRef.current) {
350+
pollingIntervalRef.current = window.setInterval(() => {
351+
processPolledData();
352+
}, 4000);
353+
}
298354

299355
const tempFilteredData = storedData.data
300-
.filter((item) => item['unique-id'] === storedData.selectedTab['unique-id'])
301-
.map((item) => item);
356+
.filter((item) => item['unique-id'] === storedData.selectedTab['unique-id']);
357+
filteredDataRef.current = tempFilteredData;
302358
setFilteredData(tempFilteredData);
303359

304360
return () => {
305-
window.clearInterval(intervalId);
361+
if (pollingIntervalRef.current) {
362+
window.clearInterval(pollingIntervalRef.current);
363+
pollingIntervalRef.current = undefined;
364+
}
306365
};
307366
}, [storedData.selectedTab, isClient, processPolledData]);
308367

309368
const selectedTabsIndex = storedData.tabs.findIndex((item) =>
310369
item === storedData.selectedTab
311370
);
312371

372+
const theme = useMemo(() => getTheme(storedData.theme), [storedData.theme]);
373+
313374
if (!isClient) {
314375
return null;
315376
}
316377

317378
return (
318-
<ThemeProvider theme={getTheme(storedData.theme)}>
319-
<GlobalStyles theme={getTheme(storedData.theme)} />
379+
<ThemeProvider theme={theme}>
380+
<GlobalStyles theme={theme} />
320381
<div className="main">
321382
<AppLoader isRegistered={isRegistered} mode={loaderAnimationMode} />
322383
{aboutPopupVisibility && (
@@ -365,7 +426,7 @@ const HomePage = () => {
365426
<TabSwitcher
366427
handleTabButtonClick={handleTabButtonClick}
367428
selectedTab={storedData.selectedTab}
368-
data={[...storedData.tabs]}
429+
data={storedData.tabs}
369430
handleAddNewTab={handleAddNewTab}
370431
handleDeleteTab={handleDeleteTab}
371432
handleTabRename={handleTabRename}
@@ -391,7 +452,7 @@ const HomePage = () => {
391452
/>
392453
</div>
393454
<RequestsTableWrapper
394-
data={[...filteredData]}
455+
data={filteredData}
395456
selectedInteraction={selectedInteraction || ''}
396457
handleRowClick={handleRowClick}
397458
filter={storedData.filter}

0 commit comments

Comments
 (0)