Skip to content

Instantly share code, notes, and snippets.

@pohy
Created April 18, 2023 12:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pohy/f2d7ce41c274eaccc268c106ba89a8c0 to your computer and use it in GitHub Desktop.
Save pohy/f2d7ce41c274eaccc268c106ba89a8c0 to your computer and use it in GitHub Desktop.
import { useRouter } from 'next/router';
export type UseQueryParamOptions<TDefaultValue> = {
defaultValue: TDefaultValue;
parse?: (value: string, defaultValue: TDefaultValue) => TDefaultValue;
};
/**
* The hook retrieves a parameter from the query string using the Next.js router.
* Accepts an optional argument with options
* First option is an optional default value
* Second option is an optional function to parse the value
*/
export function useQueryParam<TDefaultValue>(
key: string,
options?: UseQueryParamOptions<TDefaultValue>,
): TDefaultValue {
const router = useRouter();
const value = router.query[key];
const parsedValue =
typeof options?.parse === 'function'
? options.parse(value as string, options?.defaultValue)
: value;
return (parsedValue ?? options?.defaultValue) as TDefaultValue;
}
export function parseNumber(value: string | undefined, defaultValue = 0): number {
if (typeof value === 'undefined') {
return defaultValue;
}
// TODO: What about floats, though? :))
const parsedValue = Number.parseInt(value, 10);
if (Number.isNaN(parsedValue)) {
return defaultValue;
}
return parsedValue;
}
export function parseBoolean<TDefaultValue>(
value: string | undefined,
defaultValue?: TDefaultValue,
) {
if (typeof value === 'undefined') {
return defaultValue;
}
if (value === 'true') {
return true;
}
if (value === 'false') {
return false;
}
return defaultValue;
}
export function parseObject<TDefaultValue>(
value: string | undefined,
defaultValue: TDefaultValue,
): TDefaultValue {
if (typeof value === 'undefined') {
return defaultValue;
}
try {
return JSON.parse(value) as TDefaultValue;
} catch {
return defaultValue;
}
}
import { useUpdateQueryParam, UseUpdateQueryParamOptions } from './useUpdateQueryParam';
import { useQueryParam, UseQueryParamOptions } from './useQueryParam';
export function useQueryParamState<S>(
key: string,
options?: UseQueryParamOptions<S> & UseUpdateQueryParamOptions,
): [ReturnType<typeof useQueryParam<S>>, ReturnType<typeof useUpdateQueryParam<S>>] {
const state = useQueryParam<S>(key, options);
const setState = useUpdateQueryParam<S>(key, options);
return [state, setState];
}
import { ParsedUrlQuery } from 'querystring';
import { useRouter } from 'next/router';
import { useCallback } from 'react';
export type UseUpdateQueryParamOptions = {
/**
* When true, uses browser history push instead of, the default, replace
* Can also be overridden when calling the set method
*/
pushHistory?: boolean;
};
/**
* The hook returns a function that updates a value to the query string using the Next.js router
* The value can be any value, and it is serialized to string
*/
export function useUpdateQueryParam<T = unknown>(
key: string,
{ pushHistory = false }: UseUpdateQueryParamOptions = {},
) {
const { isReady, pathname, asPath, query, replace: replaceRoute, push: pushRoute } = useRouter();
const updateUrlQueryParam = useCallback(
async (nextQuery: ParsedUrlQuery, usePushHistory: boolean) => {
if (!isReady) {
return false;
}
// Filter out query values included in the dynamic route
const queryWithoutDynamicRouteParams = Object.entries(nextQuery ?? {}).reduce(
(acc, [key, value]) => {
if (pathname.includes(`[${key}]`)) {
return acc;
}
return { ...acc, [key]: value };
},
{},
);
const updateRoute = pushHistory || usePushHistory ? pushRoute : replaceRoute;
return updateRoute({
pathname: asPath.split('?')[0],
query: queryWithoutDynamicRouteParams,
});
},
[asPath, isReady, pathname, pushHistory, pushRoute, replaceRoute],
);
return useCallback(
async (value: T, { usePushHistory = pushHistory }: { usePushHistory?: boolean } = {}) => {
if (typeof value === 'undefined') {
const queryWithoutKey = { ...query };
delete queryWithoutKey[key];
return updateUrlQueryParam(queryWithoutKey, usePushHistory);
}
return updateUrlQueryParam(
{
...query,
[key]: typeof value !== 'string' ? JSON.stringify(value) : value,
},
usePushHistory,
);
},
[pushHistory, query, updateUrlQueryParam, key],
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment