react-router icon indicating copy to clipboard operation
react-router copied to clipboard

[Docs]: for useSearchParams, functional updates should be explained in more detail

Open YakovL opened this issue 1 year ago • 5 comments

Describe what's incorrect/missing in the documentation

Looking at https://reactrouter.com/en/main/hooks/use-search-params, one may think that to update one param while keeping the others, one can do:

const [searchParams, setSearchParams] = useSearchParams()

//...
const setPageNumber = (pageNumber: number) => setSearchParams(params => ({ ...params, page: String(pageNumber) }))

to update just the page portion of the query. However, this doesn't work: it removes all the query params and sets just the page portion. (not sure if this is a bug or an intended behavior)

To work around this, one has to do:

const setPageNumber = (pageNumber: number) => setSearchParams(params => {
  params.set('page', String(pageNumber))
  return params
})

I think this is not trivial, and hence should be documented (unless it's a bug, I'm using 6.22.3).

YakovL avatar Apr 15 '24 09:04 YakovL

URLSearchParams is not a regular object, so you can't spread it like you normally would.

Instead, you need to convert the existing params into an object via Object.fromEntries()

const [searchParams, setSearchParams] = useSearchParams()

//...
const setPageNumber = (pageNumber: number) => 
  setSearchParams(params => ({ ...Object.fromEntries(params), page: String(pageNumber) }))

https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/URLSearchParams

kiliman avatar Apr 15 '24 11:04 kiliman

IMHO the bigger undocumented caveat is that any mutation of searchParams (the first array entry returned), e.g. searchParams.delete('page') has the side effect of actually changing the URL.

depoulo avatar Apr 15 '24 14:04 depoulo

Yeah, both are relevant: the Object.fromEntries trick would be helpful there, and then an explanation of how to properly delete a param would be also useful. Is

setSearchParams(params => {
  const queryParams = Object.fromEntries(params)
  delete queryParams['page']
  return queryParams
})

sufficient to avoid side-effects?

YakovL avatar Apr 15 '24 14:04 YakovL

Just a warning: Object.fromEntries will return an incomplete copy if the URLSearchParams contains array values (e.g. ?a=4&a=5 will be collapsed to {a: 5}. Check the MDN link from earlier in the thread for how to use .entries() of the URLSearchParams instead, though in the case of a simple deletion this is cleaner:

const newParams = new URLSearchParams(params)
newParams.delete('page')
return newParams

zerosym avatar Aug 29 '24 02:08 zerosym

IMHO the bigger undocumented caveat is that any mutation of searchParams (the first array entry returned), e.g. searchParams.delete('page') has the side effect of actually changing the URL.

This is a huge omission, and your comment here is what allowed me to solve a bug in my app that was having me tear out my remaining hair.

ecline123 avatar Oct 21 '24 19:10 ecline123