Hooks proposal
I have written 2 hooks that we've been finding pretty useful at work, I was wondering whether they might be of interest to the community. They're fully type-safe like native tanstack/router hooks (except for route masking because we don't use that, but that's seems doable if needed).
The first is
useNavigatePreload, it works like useNavigate, but in 2 steps, first to preload, then to actually navigate.
Here, we need to navigate programmatically because it needs to happen after a "code event" (a mutation here), and not a "user action" (where we would use a regular link). But we still want some preloading. So the preload() call will do a router.loadRouteChunk under the hood (not actually call the loaders, because the mutation is likely to change things in the destination route). Having navigate tied to preload ensures we preload the correct route.
The second is useSearchState. It's based on useSearch but behaves like a useState.
This hook makes it very easy to manipulate search params in a way that is familiar to react devs. It also batches calls to the setState into a single navigate call to minimize work by the router.
I can provide the actual implementation if someone wants either of those. Or maybe seeing the idea is helpful enough to someone.32 Replies
vicious-gold•7mo ago
about
useNavigatePreload, why do you need this? navigate would also cause the route chunk to be loaded if not done so yet
useSearchState might be interesting, yes. i'll paste a version that @TkDodo 🔮 once wroteafraid-scarletOP•7mo ago
that's because of situations like in the example: we know where we want to navigate before we actually want to navigate. So that gives us an opportunity to preload the route
vicious-gold•7mo ago
yeah but still, what does this actually do mean in terms of UX/UI?
it just means you dont trigger the pendingComponent since you are preloading
but it will not be faster
afraid-scarletOP•7mo ago
it just means we're loading the JS chunks while the mutation is ongoing. Yeah it's not much, but it's so easy to use that we might as well.
vicious-gold•7mo ago
ah i see, you parallelize route chunk loading with mutation
afraid-scarletOP•7mo ago
exactly
vicious-gold•7mo ago
maybe a bit too niche for a core hook
we should do it shadcn style, copy the hook in your project if you want it
whereas the useSearchState is more likely to be used i think
but still, there might be different tastes
this is why we held off with those higher level hooks for now
afraid-scarletOP•7mo ago
TkDodo's useSearchState is simpler than mine! I might have gotten a little lost in the types, but I also have batching which I think is pretty nice for a hook like this (since a useState would also batch subsequent calls)
afraid-scarletOP•7mo ago
yeah I definitely get it, I just though I might add my grain of salt to the conversation
vicious-gold•7mo ago
absolutely appreciated
thats why I think a different distribution mechanism than putting it in a library is probably best here
afraid-scarletOP•7mo ago
ah ah I don't like the shadcn style distribution, I always get FOMO that I don't have the latest version of whatever stuff I copied. But ppl seem to enjoy it.
vicious-gold•7mo ago
lol
but you already see there are many ways to rome here for e.g. useSearchState
afraid-scarletOP•7mo ago
yeah but mine's better (i'm just trolling)
thanks for the chat
vicious-gold•7mo ago
i would really like some user contributed high level hook collection somewhere (outside of discord)
afraid-scarletOP•7mo ago
since I wrote those hooks, I think they broke at least 2 times on minor releases of tanstack/router, maybe when the lib reaches a slightly more stable state then it might be more reasonnable to have ppl copy/paste some high level hooks
vicious-gold•7mo ago
how did they break?
types?
or runtime
afraid-scarletOP•7mo ago
yeah types only
vicious-gold•7mo ago
ok, thats kinda expected when reaching deep into the router types
no guarantuees there
afraid-scarletOP•7mo ago
I'm not blaming the release strategy, I'm ok w/ it, but if we have this kind of hook copy/pasted in many codebases, we can't "remote fix" them
vicious-gold•6mo ago
sure
slippery slope
coming back to this
why do we need the "batching" part?
afraid-scarletOP•6mo ago
Sorry I never saw those last messages. The batching is so it behaves like a react
useState:
In this example, if setFoo and setBar come from useState, this will only cause a single render. In this same vein, if they come from useSearchState, they should only cause 1 navigation.vicious-gold•6mo ago
what happens without the batching and multiple set calls from useSearchState ?
multiple renders?
we should get this into the main repo
flat-fuchsia•6mo ago
Hi!
Super cool topic, was looking for the same thing.
I was tired of writing the same setter with (prev) => {...prev, etc.} all the time.
So +1 on having this in the lib in any way 🙂
In the meantime, I was wondering:
- Would you be able to share a version of the pasted useSearchState that compiles, with all imports & all (the one from tkDodo ?)
- On bostonsheraff's version, for my information, how come you need the binding and all? From your version I was able to get it to work with only this:
And adding my two cents, here is a helper returning a function to get the setter from the key, using currying:
Any comments or suggestions welcomed!
afraid-scarletOP•6mo ago
I'm using bind to avoid recreating function scopes on every re-render. This is a minor optimisation, but since this kind of hook usually goes in components that re-render pretty often, it felt like a free win
I don't remember well, I should test it to give an answer. But at least 2 navigations (2 history replaces, or if you push, 2 actual history entries), I don't remember if it does re-render twice or not.
flat-fuchsia•6mo ago
Hi !
Adding my 2 cents again because I've been playing with the matter recently and found the topic to be fun:
I've come up with a new helper that generates the various (independent) setters for the search params, from the route, and properly typed.
It allows to consume them like this:
This way I can stay close to the way the framework is expressed but just have a helper for all these navigates.
The implementation is a bit clunky because I had to access the validateSearch object to reconstruct the setters from the keys, and they're not typed, but I'm sure it could be improved.
I'd be very happy to have your opinion on this, suggestions to improve it, etc !
Thanks a lot
afraid-scarletOP•6mo ago
I don't think this can work for every validateSearch, since it can be many things other than zod. Maybe with a proxy it could be made more generic. This is interesting though.
flat-fuchsia•6mo ago
indeed, that's definitely one of the shortcoming of such implementation, which works for me in practice but isn't general enough. But I guess knowing the innards of tanstack-router it should be possible to write it in a cleaner way.
afraid-scarletOP•5mo ago
Here's a first draft for a
useSearchState hook: https://github.com/TanStack/router/pull/4552
Sorry for the delay, I was playing Clair Obscur Expedition 33GitHub
feat(react-router): useSearchState by Sheraff · Pull Request #4552...
This is a 1st draft proposal for useSearchState: a hook designed to be used like a react useState, but manipulates route search params instead.
Example usage:
export const Route = createFileRoute(&...
afraid-scarletOP•5mo ago
The implementation is actually better than what I initially proposed in this thread, because this PR made me write unit tests!
vicious-gold•5mo ago
cc @TkDodo 🔮
afraid-scarletOP•5mo ago
also, please don't do a commit-by-commit review, because I mistakenly forked an old fork of the repo... The final diff on github is correct though, no worries
just FYI, i updated our implementation of useSearchState at work to use the same one as in this PR. If there's something fundamentally wrong with this, we'll know!
flat-fuchsia•3mo ago
Hey,
Just dropping in to see what's going on on this topic.
Do you plan to merge this work and offer a useSearchState anytime soon?
On my end, we've been using my
useSetSearch helper at my company for a few months now, and it has been behaving like a charm, and feels quite pleasing to read.
Would you consider adding this to the library if I make a proper PR, or do you think it should stay a side helper?
thanks !