useLocation() returns the current location object and re-renders the component on every navigation. It has five fields: pathname, search (the query string, leading ? included), hash, state (whatever a <Link> or navigate call attached, or null), and key, a unique id per history entry that makes a fine cache key or a useEffect dependency for "run again on every navigation".
The query string is not parsed for you: search is a string and URLSearchParams is the browser's parser. useSearchParams wraps reading and writing it in one hook shaped like useState.
function Catalog() {
const [params, setParams] = useSearchParams({ sort: 'name' }); // default, not in the URL
const tags = params.getAll('tag'); // ?tag=a&tag=b
return <>
<select value={params.get('sort')}
onChange={(e) => setParams({ sort: e.target.value })}>
<option value="name">Name</option><option value="price">Price</option>
</select>
<button onClick={() => setParams((p) => { p.delete('tag'); return p; },
{ replace: true })}>Clear {tags.length} tags</button>
</>;
}The setter accepts a string, an object, an array of pairs, a URLSearchParams, or a function that receives the current params and returns them mutated — use the functional form whenever the new value depends on the old. Its second argument takes replace, state and preventScrollReset. Pass replace: true for anything a user adjusts repeatedly, such as a slider or a search box; otherwise every keystroke becomes a history entry and Back turns into an undo log.
Filters, sort order and pagination in the query string cost one hook and buy a shareable, bookmarkable, reload-proof view. Keep private UI state — a dropdown's open flag, an unsaved draft — in useState.