Skip to content

Commit

Permalink
Merge pull request #2498 from headlamp-k8s/map-use-query-params-hook
Browse files Browse the repository at this point in the history
frontend: Add useQueryParamsState hook
  • Loading branch information
illume authored Oct 31, 2024
2 parents 320383f + 24e64e9 commit a6d5827
Showing 1 changed file with 76 additions and 0 deletions.
76 changes: 76 additions & 0 deletions frontend/src/components/resourceMap/useQueryParamsState.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { useCallback, useEffect, useState } from 'react';
import { useHistory, useLocation } from 'react-router';

type UseQueryParamsStateReturnType<T> = [T | undefined, (newValue: T | undefined) => void];

/**
* Custom hook to manage a state synchronized with a URL query parameter
*
* @param param - The name of the query parameter to synchronize with
* @param initialState - The initial state value
* @returns A tuple containing the current state value and a function to update the state value
*
* @example
* const [searchTerm, setSearchTerm] = useQueryParamsState('search', 'initial-value')
*
*/
export function useQueryParamsState<T extends string | undefined>(
param: string,
initialState: T
): UseQueryParamsStateReturnType<T> {
const location = useLocation();
const history = useHistory();

// State for managing the value derived from the query parameter
const [value, setValue] = useState<T | undefined>(() => {
const { search } = location;
const searchParams = new URLSearchParams(search);
const paramValue = searchParams.get(param);

return paramValue !== null ? (decodeURIComponent(paramValue) as T) : initialState;
});

// Update the value from URL to state
useEffect(() => {
const searchParams = new URLSearchParams(location.search);
const paramValue = searchParams.get(param);

if (paramValue !== null) {
const decodedValue = decodeURIComponent(paramValue) as T;
setValue(decodedValue);
} else {
setValue(undefined);
}
}, [location.search]);

// Set the value from state to URL
useEffect(() => {
const currentSearchParams = new URLSearchParams(location.search);

if (value && currentSearchParams.get(param) === encodeURIComponent(value)) return;

// Update the query parameter with the current state value
if (value !== null && value !== '' && value !== undefined) {
currentSearchParams.set(param, encodeURIComponent(value));
} else {
currentSearchParams.delete(param);
}

// Update the URL with the modified search parameters
const newUrl = [location.pathname, currentSearchParams.toString()].filter(Boolean).join('?');

history.push(newUrl);
}, [param, value]);

const handleSetValue = useCallback(
(newValue: T | undefined) => {
if (typeof newValue !== 'string') {
throw new Error("useQueryParamsState: Can't set a value to something that isn't a string");
}
setValue(newValue);
},
[setValue]
);

return [value, handleSetValue];
}

0 comments on commit a6d5827

Please sign in to comment.