5

Beginner here, but finding this quite tricky. So some help would be appreciated!

I want to have users filter through some options. Those filters should be reflected in the URL. e.g. : http://localhost:3000/items?counter=1

Now when a user visits http://localhost:3000/items?counter=2 I want that to be reflected in state & put it in state. If the same user then changes the state somehow, I want that to be reflected in the url. I do know how to do both things.

But I feel I am running into an infinite loop here:

    useEffect(() => {
        router.push(`/items?counter=${counter}`, undefined, { shallow: true })
    }, [counter])

    useEffect(() => {
        setCounter(parseInt(router.query.counter))
    }, [router.query.counter])

How would I best derive my state from my query params but also always shallow-update the query params every time state changes?

antonwilhelm
  • 5,768
  • 4
  • 19
  • 45

2 Answers2

4

Always update only one of them, and update the other by listening to the changes of the first. Since the state is always derived from the query, I would update the state via useEffect, and always change the query directly.

This means that you don't update the state directly. Whenever you want to update the state, you need to update the query:

const updateCounterQuery = currentCounter => router.push(`/items?counter=${currentCounter}`, undefined, { shallow: true })

useEffect(() => {
  setCounter(parseInt(router.query.counter))
}, [router.query.counter])

However, why do you even need a state in this case? Always use the value that you get from the query:

const updateCounterQuery = counter => router.push(`/items?counter=${counter }`, undefined, { shallow: true })

const counter = +router.query.counter
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
  • Thanks for your answer. The last code snippet you posted, would I be doing this in a useeffect? Also: I wouldn't do this with shallow routing, right? Because then my component wouldn't re-render? Or am I not getting something? – antonwilhelm Oct 28 '22 at 13:42
  • No. You don't need `useEffect`. The component re-renders because `useRouter()` triggers a render, and you get a new router object. Shallow routing doesn't prevent rerender, so it would work. The only thing this code does is assigning a const. – Ori Drori Oct 28 '22 at 14:43
3

For more complex use cases where multiple query strings (e.g. ?counter=2&filter=true) or simultaneous updates (e.g. ?latitude=47.367&longitude=8.543) are required, it usually makes sense to abstract the logic to a hook.

Libraries like next-usequerystate solve exactly this use case. Guides and examples like this or this are good entrypoints for custom developments.

swimmer
  • 1,971
  • 2
  • 17
  • 28