I'm currently on react-router v5 in a running project and cannot easily migrate to v6.
I wrote a hook that allows to read and modify a single URL param while leaving the other URL params untouched.
Arrays are treated as lists of comma separated values:
?products=pipe,deerstalker,magnifying_glass
import { useCallback } from 'react';
import { useHistory } from 'react-router';
const getDecodedUrlParam = (name: string, locationSearch: string, _default?: any) => {
  const params = deserialize(locationSearch);
  const param = params[name];
  if (_default && Array.isArray(_default)) {
    return param
      ? param.split(',').map((v: string) => decodeURIComponent(v))
      : _default;
  }
  return param ? decodeURIComponent(param) : _default;
};
const deserialize = (locationSearch: string): any => {
  if (locationSearch.startsWith('?')) {
    locationSearch = locationSearch.substring(1);
  }
  const parts = locationSearch.split('&');
  return Object.fromEntries(parts.map((part) => part.split('=')));
};
const serialize = (params: any) =>
  Object.entries(params)
    .map(([key, value]) => `${key}=${value}`)
    .join('&');
export const useURLSearchParam = (name: string, _default?: any) => {
  const history = useHistory();
  const value: any = getDecodedUrlParam(name, location.search, _default);
  const _update = useCallback(
    (value: any) => {
      const params = deserialize(location.search);
      if (Array.isArray(value)) {
        params[name] = value.map((v) => encodeURIComponent(v)).join(',');
      } else {
        params[name] = encodeURIComponent(value);
      }
      history.replace({ pathname: location.pathname, search: serialize(params) });
    },
    [history, name]
  );
  const _delete = useCallback(() => {
    const params = deserialize(location.search);
    delete params[name];
    history.replace({ pathname: location.pathname, search: serialize(params) });
  }, [history, name]);
  return [value, _update, _delete];
};