#useFetcher Vs URL best practices?

1 messages ยท Page 1 of 1 (latest)

astral saddle
#

Hi folks! New to remix at work, we've started using it a few weeks ago (remix 2), so still reading and re-reading the docs.

One thing I'm struggling to fully wrap my head around is how best to use the useFetcher (or useSubmit) Vs query params or changing the URL.

To give an example and make it easier to explain perhaps, we have a table with our data, that has some buttons that act as data filters. So I'm using the route loader to request the table data. No worries there.

When the user clicks one of the filter buttons, it requests the matching data from the backend. So at first, I thought as this wouldn't be a change in navigation, I would need to use a fetcher. Probably fetcher.load()?

But then the table wasn't rerendering with the new data because I misunderstood that the result of the use fetcher doesn't change the data from useLoaderData(). So it would be possible to conditionally use the fetcher data, and if undefined, use the loader data but this felt a bit fiddly and probably not correct.

So the next option I tried was to adjust the URL directly. So when the filter button gets clicked, it changes the URL directly and appends a query argument which the loader picks up, and because the URL changes, the loaders auto revalidate and the table gets the new data.

This works and feels more like the remix way? But still a bit confused when to use the fetcher precisely, or if this URL change isn't the best way and there could be another? ๐Ÿ˜…

Similarly, the table has a search input, which would need to query the backend again to filter the returned results (as the table is paginated). So would the best way to do this also be the URL query args and rely on remix's auto cancellation? Debounce? Or a fetcher (submit or load? ๐Ÿ˜…)

Any pointers, suggestions, or extra reading much appreciated!

true flicker
#

There the team explains when to use fetcher vs form/useSubmit

astral saddle
#

I did yeah, few times just in case, but my specific scenario didn't quite match the examples given.

For instance, I'm not creating or deleting a record, and not navigating to a different page - but I'm not loading data for a popover, and while a URL change isn't necessary, it doesn't matter too much if I'm adding new query params. So it kinda fits into both URL change not desired and URL change desired.

I guess it falls mostly into the URL change not desired, which suggests to useFetcher, but that implementation felt a little awkward compared with using the query args and letting the loader do it's thing.

So don't think I quite understand the best way forward generally

#

That page seems to focus on actions rather than loaders, so perhaps I'm misunderstanding how to do this more generally, so it could be that it should be an action? or I should think about it differently perhaps

true flicker
#

search params are used to build things like filters, which is your case, so even if you don't navigate to a different route you're navigating to the same route with different search params which affect the loader data

#

think of search params as a state, instead of doing setState you change the search params, but changing the URL cause a navigation

astral saddle
#

So if I'm understanding you correctly, using the URL to apply search params to invoke the loaders to rerender with the latest data sounds like the correct path then?

fetchers/forms discussed on the doc should be used more for mutations/actions?

So would you use the URL as state for the search input as well, allowing it to adjust a query param for instance, and return the results for that in the same route?

true flicker
#

using the URL to apply search params to invoke the loaders to rerender with the latest data sounds like the correct path then?
yes

#

forms can be used for doing GET or POST navigations, a POST for a mutation and a GET to update the search params (useful for filters, sorting, search)

#

fetchers are useful when you don't want to change the URL or do a navigation

#

e.g. if you want to run a mutation on every item of a list

#

So would you use the URL as state for the search input as well, allowing it to adjust a query param for instance, and return the results for that in the same route?
yes

astral saddle
#

Okay, that makes sense, thank you so much.

And sorry, one more question just so I'm clear in my head ๐Ÿ˜… fetcher won't adjust the data in useLoaderData, only ever via its own .data prop. But that can be accessed elsewhere in the tree by using keys.

And if so, if there was a business requirement to not adjust the URL at all for the filter, would conditionally using fetcher.data || useLoaderData().thing be an actual correct usage of that? Or is that a weird thing to do ๐Ÿ˜„

true flicker
#

it's not a bad thing, you could consider the loader data as a default value in case fetcher.data is undefined

#

imagine it like let [thing, setThing] = useState(loaderData.thing)

#

you start with the loaderData.thing value and then use a separate state

#

but you need to be sure that you only want the loader data as initial value

#

remember that any mutation will trigger a revalidation so the loader data will update but after your fetcher has data it won't use the loaderData value anymore

astral saddle
#

Ah, ya, very true. I wondered if there was a fetcher reset to use before firing the mutation in that instance, which led me to https://github.com/remix-run/remix/discussions/2749 and that heavily implies that fetcher data should be used to update component state rather than used directly, so that's useful to know too.

Great! Thanks so much for the help

GitHub

What is the new or updated feature that you are suggesting? Reset the fetcher data returned from the action? Using something Like fetcher.reset() Why should this feature be included? Example Flow w...

#

(moot for now as URL search sounds like the path forward, but useful learning!)

true flicker
#

I don't recommend you to update component state based on fetcher.data

#

that's a React anti pattern, even described on React docs

#

usually resetting a fetcher is because the component that holds the useFetcher instance represents a different entity

#

e.g. a new modal instance

#

or a new post to edit in a blog

#

in that case it's better to just trigger a re-mount of the component

#

by using a key

astral saddle
#

Hmm, so I guess that doesn't help with the scenario above if we were using loader data as default value, then fetcher gets populated, then a mutation occurs ๐Ÿค” unless the key was adjusted before mutating I suppose? Or using the action workaround in that GH to return null data

true flicker
#

if you used fetcher.load instead of fetcher.submit, then on a revalidation the fetcher will also revalidate

astral saddle
#

Ah, ok ok! So could use the same fetcher for the mutation, and trigger the revalidation. That would avoid the loader/fetcher data discrepancy

true flicker
#

don't use the same fetcher instance for loading data and triggering a mutation

#

those should be two different loaders

#

think of them as loaderData vs actionData