You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
overseerr/src/hooks/useUpdateQueryParams.ts

152 lines
3.9 KiB

import type { NextRouter } from 'next/router';
import { useRouter } from 'next/router';
import type { ParsedUrlQuery } from 'querystring';
import { useCallback } from 'react';
type UseQueryParamReturnedFunction = (
query: ParsedUrlQuery,
routerAction?: 'push' | 'replace'
) => void;
interface MergedQueryString {
pathname: string;
path: string;
}
/**
* Returns a filtered object containing only key/value pairs that don't exist in the current
* router path.
*
* @param router Nextjs router instance
* @param filters Object containing key value pairs for filter items that should be cleaned
*/
export const filterQueryString = (
router: NextRouter,
filters: ParsedUrlQuery
): ParsedUrlQuery => {
const cleanedFilters: ParsedUrlQuery = {};
Object.keys(filters).forEach((key) => {
if (!router.pathname.match(new RegExp(`${key}`))) {
cleanedFilters[key] = filters[key];
}
});
return cleanedFilters;
};
/**
* Takes a query paramter object and returns a new pathname and path
* with the new values appended.
*
* - If the value already exists, it is updated.
* - If a key with the value of null is passed, it will be removed from
* the current query paramters
*
* ## Example usage:
*
* If the current URL is `/foo?bar=test` and you want to add a new query parameter, you
* can do the following:
*
* ```
* const newRoute = mergeQueryString(router, { newParam: 'value' });
* ```
* NewRoute will become
*
* ```
* {
* pathName: '/foo?bar=test&newParam=value',
* path: '/foo?bar=test&newParam=value'
* }
* ```
*
* @param router Nextjs router instance
* @param query Key/value pair object containing query paramters
*/
export const mergeQueryString = (
router: NextRouter,
query: ParsedUrlQuery
): MergedQueryString => {
const cleanedQuery = filterQueryString(router, router.query);
const mergedQuery = Object.assign({}, cleanedQuery, query);
const queryArray: string[] = [];
Object.keys(mergedQuery).map((key) => {
if (mergedQuery[key]) {
queryArray.push(`${key}=${mergedQuery[key]}`);
}
});
const pathWithoutQuery = router.asPath.match(/(.*)\?.*/);
const asPath = pathWithoutQuery ? pathWithoutQuery[1] : router.asPath;
const pathname = `${router.pathname}${
queryArray.length > 0 ? `?${queryArray.join('&')}` : ''
}`;
const path = `${asPath}${
queryArray.length > 0 ? `?${queryArray.join('&')}` : ''
}`;
return { pathname, path };
};
/**
* useQueryParams hook is used just to provide a callback with a nextjs
* router instance attached to it. The returned method can be called with
* an object of key/value pairs to route the user with the new query paramters
*/
export const useQueryParams = (): UseQueryParamReturnedFunction => {
const router = useRouter();
return useCallback(
(query: ParsedUrlQuery, routerAction: 'push' | 'replace' = 'push') => {
const newRoute = mergeQueryString(router, query);
if (newRoute.path !== router.asPath) {
if (routerAction === 'replace') {
router.replace(newRoute.pathname, newRoute.path);
} else {
router.push(newRoute.pathname, newRoute.path);
}
}
},
[router]
);
};
export const useUpdateQueryParams = (
filter: ParsedUrlQuery
): ((key: string, value?: string) => void) => {
const updateQueryParams = useQueryParams();
return useCallback(
(key: string, value?: string) => {
const query = {
...filter,
[key]: value,
};
updateQueryParams(query, 'replace');
},
[filter, updateQueryParams]
);
};
export const useBatchUpdateQueryParams = (
filter: ParsedUrlQuery
): ((items: Record<string, string | undefined>) => void) => {
const updateQueryParams = useQueryParams();
return useCallback(
(items: Record<string, string | undefined>) => {
const query = {
...filter,
...items,
};
updateQueryParams(query, 'replace');
},
[filter, updateQueryParams]
);
};