Skip to content

Instantly share code, notes, and snippets.

@ramirezsandin
Last active July 4, 2024 17:11
Show Gist options
  • Save ramirezsandin/cc93f6a405ff818e48038e28a26a1ba0 to your computer and use it in GitHub Desktop.
Save ramirezsandin/cc93f6a405ff818e48038e28a26a1ba0 to your computer and use it in GitHub Desktop.
React hook to be used with Next.js, it uses useRouter internally and offers some helper functions to manipulate the url query params.
import { useRouter } from "next/router";
import { ParsedUrlQuery } from "querystring";
interface UseRouterParamsOptions {
method?: "push" | "replace";
shallow?: boolean;
}
const useRouterParams = (options?: UseRouterParamsOptions) => {
const { query, pathname, push, replace } = useRouter();
const reload = options?.method === "replace" ? replace : push;
const shallow = options?.shallow ?? true;
/**
* Checks whether a param is exposed in the URL string or not.
* @param name The name of the param.
* @param value Optional, the param must have the specified value.
* @returns true/false depending on the presence of the param.
*/
const hasParam = (name: string, value?: string | number | boolean) => {
const { [name]: param } = query;
if (!value) {
return !!param;
}
if (!param) {
return false;
} else if (Array.isArray(param)) {
return param.indexOf(encodeURIComponent(value)) > -1;
} else {
return param === encodeURIComponent(value);
}
};
/**
* Retrieves from the URL the value of the provided param.
* @param name The name of the param.
* @returns The value of the param.
*/
const getParamValue = (name: string) => {
const value = query[name];
return !value
? value
: Array.isArray(value)
? value.map((el) => decodeURIComponent(el))
: decodeURIComponent(value);
};
/**
* Adds a query param to the URL string. Multiple params with the same name
* and different values can be added.
* @param name The name of the param.
* @param value The value of the param.
*/
const addParam = (name: string, value: string | boolean | number) => {
const { [name]: param, ...rest } = query;
let newQuery;
if (!param) {
newQuery = { ...rest, [name]: encodeURIComponent(value) };
} else if (Array.isArray(param)) {
if (param.indexOf(encodeURIComponent(value)) > -1) return;
newQuery = { ...rest, [name]: [...param, encodeURIComponent(value)] };
} else {
if (param === encodeURIComponent(value)) return;
newQuery = { ...rest, [name]: [param, encodeURIComponent(value)] };
}
reload(
{
pathname,
query: newQuery,
},
undefined,
{ shallow }
);
};
/**
* It sets a query param in the URL to a given value. If it already exists, it
* will be overriden.
* @param name The name of the param.
* @param value The value of the param, it can be single or multiple values.
*/
const setParam = (
name: string,
value?: string | boolean | number | string[] | boolean[] | number[]
) => {
if (!value) {
removeParam(name);
return;
}
const { [name]: param, ...rest } = query;
reload(
{
pathname,
query: {
...rest,
[name]: Array.isArray(value)
? value.map((el) => encodeURIComponent(el))
: encodeURIComponent(value),
},
},
undefined,
{ shallow }
);
};
/**
* If no argument is passed, it clears all the query params from the URL.
* If one or more params are passed as arguments, only those will be cleared
* from the URL.
* @param params one or more params to remove.
*/
const clearParams = (...params: string[]) => {
// Clear all params
if (!params.length) {
reload(
{
pathname,
},
undefined,
{ shallow }
);
return;
}
// Clear the given params
const newQuery = Object.keys(query).reduce((acc, curr) => {
if (params.indexOf(curr) === -1) {
acc[curr] = query[curr];
}
return acc;
}, {} as ParsedUrlQuery);
reload(
{
pathname,
query: newQuery,
},
undefined,
{ shallow }
);
};
/**
* Removes the provided params with a specific value from the URL.
* @param name The name of the param.
* @param value The value of the param.
*/
const removeParam = (
name: string,
value?: string | number | boolean | string[] | number[] | boolean[]
) => {
const { [name]: param, ...rest } = query;
if (!param) {
return;
}
let newQuery;
if (value && Array.isArray(param) && !Array.isArray(value)) {
newQuery = {
...rest,
[name]: param.filter(
(element) => element !== encodeURIComponent(value)
),
};
} else {
newQuery = { ...rest };
}
reload(
{
pathname,
query: newQuery,
},
undefined,
{ shallow }
);
};
/**
* Adds the query param to the URL if it's not already there or removes it
* otherwise.
* @param name The name of the param.
* @param value The value of the param.
*/
const toggleParam = (name: string, value: string | boolean | number) => {
const { [name]: param, ...rest } = query;
let newQuery;
if (!param) {
// It doesn't exists -> add it.
newQuery = { ...rest, [name]: encodeURIComponent(value) };
} else if (Array.isArray(param)) {
if (param.indexOf(encodeURIComponent(value)) > -1) {
// There are multiple values for the same param and the value it's there
// -> remove the value.
newQuery = {
...rest,
[name]: param.filter(
(element) => element !== encodeURIComponent(value)
),
};
} else {
// There are multiple values for the same param and the value it's not there
// -> add the new value.
newQuery = { ...rest, [name]: [...param, encodeURIComponent(value)] };
}
} else {
if (param === encodeURIComponent(value)) {
// The param exists with the same value -> remove it from the url.
newQuery = { ...rest };
} else {
// The param exists with other values -> add the value.
newQuery = { ...rest, [name]: [param, encodeURIComponent(value)] };
}
}
reload(
{
pathname,
query: newQuery,
},
undefined,
{ shallow }
);
};
return {
hasParam,
getParamValue,
addParam,
setParam,
clearParams,
removeParam,
toggleParam,
};
};
export { useRouterParams };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment