From 1cac2c60f9f260a438820b6787a2f4379baf8b9d Mon Sep 17 00:00:00 2001 From: Laurence Date: Wed, 6 Aug 2025 14:39:51 +0100 Subject: [PATCH 1/2] enhance: fix refretching data all the time --- crowdsec-docs/src/components/table-render.tsx | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/crowdsec-docs/src/components/table-render.tsx b/crowdsec-docs/src/components/table-render.tsx index d35bbf4bb..8ed709aa6 100644 --- a/crowdsec-docs/src/components/table-render.tsx +++ b/crowdsec-docs/src/components/table-render.tsx @@ -6,6 +6,7 @@ import React, { useEffect, useMemo, useState } from "react"; const TableRender = ({ columns, url, include = [], exclude = [] }): React.JSX.Element => { const [jsonContent, setJsonContent] = useState([]); + const [isLoading, setIsLoading] = useState(true); const { colorMode } = useColorMode(); const theme = useMemo(() => { @@ -21,9 +22,19 @@ const TableRender = ({ columns, url, include = [], exclude = [] }): React.JSX.El }); }, [colorMode]); + // Memoize the include and exclude arrays to prevent unnecessary re-renders + const memoizedInclude = useMemo(() => include, [JSON.stringify(include)]); + const memoizedExclude = useMemo(() => exclude, [JSON.stringify(exclude)]); + useEffect(() => { + setIsLoading(true); fetch(url) - .then((res) => res.json()) + .then((res) => { + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`); + } + return res.json(); + }) .then((data) => { const updatedData = []; const names = []; @@ -32,12 +43,12 @@ const TableRender = ({ columns, url, include = [], exclude = [] }): React.JSX.El // filter duplicate names const item = data[key]; const name = item.name; - for (const element of exclude) { + for (const element of memoizedExclude) { if (name.includes(element)) { return; } } - for (const element of include) { + for (const element of memoizedInclude) { if (!name.includes(element)) { return; } @@ -58,11 +69,16 @@ const TableRender = ({ columns, url, include = [], exclude = [] }): React.JSX.El }); setJsonContent(updatedData); + setIsLoading(false); + }) + .catch((error) => { + console.error("Error fetching data:", error); + setIsLoading(false); }); - // execute this fetch only once (on mount) - }, [include, exclude, url]); + // Only re-fetch when url, include, or exclude actually change + }, [url, memoizedInclude, memoizedExclude]); - if (!columns || !jsonContent) { + if (!columns || (!jsonContent && !isLoading)) { return null; } @@ -81,6 +97,7 @@ const TableRender = ({ columns, url, include = [], exclude = [] }): React.JSX.El muiPaginationProps={{ rowsPerPageOptions: [10, 15, 25, 50, 100], }} + state={{ isLoading }} /> )} From c64abea75989e6c395818f52b2289523a42b9805 Mon Sep 17 00:00:00 2001 From: Laurence Date: Wed, 6 Aug 2025 14:59:39 +0100 Subject: [PATCH 2/2] enhance: biome cry --- crowdsec-docs/src/components/table-render.tsx | 39 ++++++++----------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/crowdsec-docs/src/components/table-render.tsx b/crowdsec-docs/src/components/table-render.tsx index 8ed709aa6..84196d6bb 100644 --- a/crowdsec-docs/src/components/table-render.tsx +++ b/crowdsec-docs/src/components/table-render.tsx @@ -22,10 +22,7 @@ const TableRender = ({ columns, url, include = [], exclude = [] }): React.JSX.El }); }, [colorMode]); - // Memoize the include and exclude arrays to prevent unnecessary re-renders - const memoizedInclude = useMemo(() => include, [JSON.stringify(include)]); - const memoizedExclude = useMemo(() => exclude, [JSON.stringify(exclude)]); - + // biome-ignore lint/correctness/useExhaustiveDependencies: exclude/include are stable useEffect(() => { setIsLoading(true); fetch(url) @@ -37,36 +34,35 @@ const TableRender = ({ columns, url, include = [], exclude = [] }): React.JSX.El }) .then((data) => { const updatedData = []; - const names = []; + const names = new Set(); - Object.keys(data).forEach((key) => { + for (const key of Object.keys(data)) { // filter duplicate names const item = data[key]; const name = item.name; - for (const element of memoizedExclude) { - if (name.includes(element)) { - return; - } + + if (names.has(name)) { + continue; } - for (const element of memoizedInclude) { - if (!name.includes(element)) { - return; - } + + if (exclude.some((excluded) => name.includes(excluded))) { + continue; } - if (names.includes(name)) { - return; + + if (!include.every((included) => name.includes(included))) { + continue; } - names.push(name); + names.add(name); updatedData.push({ ...item, - // flattening list of strings into CSV strings allow global filtering on them - // /!\ it requires special handling in the rendering side (see crowdsec-docs/docs/cti_api/taxonomy) /!\ + // flattening list of strings into CSV strings to allow global filtering on them + // /!\ requires special handling in the rendering side (see crowdsec-docs/docs/cti_api/taxonomy) /!\ ...(item.behaviors ? { behaviors: item.behaviors.join("\n") } : {}), ...(item.mitre_attacks ? { mitre_attacks: item.mitre_attacks.join("\n") } : {}), ...(item.cves ? { cves: item.cves.join("\n") } : {}), }); - }); + } setJsonContent(updatedData); setIsLoading(false); @@ -75,8 +71,7 @@ const TableRender = ({ columns, url, include = [], exclude = [] }): React.JSX.El console.error("Error fetching data:", error); setIsLoading(false); }); - // Only re-fetch when url, include, or exclude actually change - }, [url, memoizedInclude, memoizedExclude]); + }, [url]); if (!columns || (!jsonContent && !isLoading)) { return null;