From ad14c2c869bdb3b2829aac80a75aa57cbcd00abf Mon Sep 17 00:00:00 2001 From: ST-Ko Date: Thu, 24 Oct 2024 01:59:10 +1100 Subject: [PATCH] refactor: move table filter logic to custom useTableFilters hook using useMemo --- .../CompareResults/ResultsTable.tsx | 54 ++--------------- src/hooks/useTableFilters.ts | 58 +++++++++++++++++++ 2 files changed, 64 insertions(+), 48 deletions(-) create mode 100644 src/hooks/useTableFilters.ts diff --git a/src/components/CompareResults/ResultsTable.tsx b/src/components/CompareResults/ResultsTable.tsx index fd0ee1586..ceb231708 100644 --- a/src/components/CompareResults/ResultsTable.tsx +++ b/src/components/CompareResults/ResultsTable.tsx @@ -1,4 +1,4 @@ -import { Suspense, useEffect, useState } from 'react'; +import { Suspense, useState } from 'react'; import Box from '@mui/material/Box'; import CircularProgress from '@mui/material/CircularProgress'; @@ -6,6 +6,7 @@ import { useSearchParams } from 'react-router-dom'; import { useLoaderData, Await } from 'react-router-dom'; import useRawSearchParams from '../../hooks/useRawSearchParams'; +import useTableFilters from '../../hooks/useTableFilters'; import type { CompareResultsItem } from '../../types/state'; import { Framework } from '../../types/types'; import type { CompareResultsTableConfig } from '../../types/types'; @@ -100,56 +101,13 @@ export default function ResultsTable() { // This is our custom hook that updates the search params without a rerender. const [rawSearchParams, updateRawSearchParams] = useRawSearchParams(); + // This is our custom hook that manages table filters + // and provides methods for clearing and toggling them. + const { tableFilters, onClearFilter, onToggleFilter } = useTableFilters(); + const initialSearchTerm = rawSearchParams.get('search') ?? ''; const [searchTerm, setSearchTerm] = useState(initialSearchTerm); const [frameworkIdVal, setFrameworkIdVal] = useState(frameworkId); - const [tableFilters, setTableFilters] = useState( - new Map() as Map>, // ColumnID -> Set - ); - - // This useEffect is to extract filter params from the URL (via rawSearchParams) - // and updates the tableFilters state. - useEffect(() => { - const filters = Array.from(rawSearchParams.entries()) - .filter(([key]) => key.startsWith('filter')) - .reduce((accumulator: Map>, [key, value]) => { - const columnId = key.split('_')[1]; - if (!accumulator.has(columnId)) { - accumulator.set(columnId, new Set()); - } - const valuesArray = value.split(',').map((item) => item.trim()); - valuesArray.forEach((item) => accumulator.get(columnId)?.add(item)); - return accumulator; - }, new Map>()); - - setTableFilters(filters); - }, [rawSearchParams]); - - const onClearFilter = (columnId: string) => { - rawSearchParams.delete(`filter_${columnId}`); - updateRawSearchParams(rawSearchParams); - - setTableFilters((oldFilters) => { - const newFilters = new Map(oldFilters); - newFilters.delete(columnId); - return newFilters; - }); - }; - - const onToggleFilter = (columnId: string, filters: Set) => { - if (filters.size > 0) { - rawSearchParams.set(`filter_${columnId}`, Array.from(filters).join(',')); - } else { - rawSearchParams.delete(`filter_${columnId}`); - } - updateRawSearchParams(rawSearchParams); - - setTableFilters((oldFilters) => { - const newFilters = new Map(oldFilters); - newFilters.set(columnId, filters); - return newFilters; - }); - }; const onFrameworkChange = (newFrameworkId: Framework['id']) => { setFrameworkIdVal(newFrameworkId); diff --git a/src/hooks/useTableFilters.ts b/src/hooks/useTableFilters.ts new file mode 100644 index 000000000..8f3cb1574 --- /dev/null +++ b/src/hooks/useTableFilters.ts @@ -0,0 +1,58 @@ +import { useMemo, useState } from 'react'; + +import useRawSearchParams from './useRawSearchParams'; + +const useTableFilters = () => { + // This is our custom hook that updates the search params without a rerender. + const [rawSearchParams, updateRawSearchParams] = useRawSearchParams(); + + const [tableFilters, setTableFilters] = useState( + new Map() as Map>, // ColumnID -> Set + ); + + useMemo(() => { + const filters = Array.from(rawSearchParams.entries()) + .filter(([key]) => key.startsWith('filter')) + .reduce((accumulator: Map>, [key, value]) => { + const columnId = key.split('_')[1]; + if (!accumulator.has(columnId)) { + accumulator.set(columnId, new Set()); + } + const valuesArray = value.split(',').map((item) => item.trim()); + valuesArray.forEach((item) => accumulator.get(columnId)?.add(item)); + return accumulator; + }, new Map>()); + + setTableFilters(filters); + }, [rawSearchParams]); + + const onClearFilter = (columnId: string) => { + rawSearchParams.delete(`filter_${columnId}`); + updateRawSearchParams(rawSearchParams); + + setTableFilters((oldFilters) => { + const newFilters = new Map(oldFilters); + newFilters.delete(columnId); + return newFilters; + }); + }; + + const onToggleFilter = (columnId: string, filters: Set) => { + if (filters.size > 0) { + rawSearchParams.set(`filter_${columnId}`, Array.from(filters).join(',')); + } else { + rawSearchParams.delete(`filter_${columnId}`); + } + updateRawSearchParams(rawSearchParams); + + setTableFilters((oldFilters) => { + const newFilters = new Map(oldFilters); + newFilters.set(columnId, filters); + return newFilters; + }); + }; + + return { tableFilters, onClearFilter, onToggleFilter }; +}; + +export default useTableFilters;