Replies: 4 comments 1 reply
-
|
Or... RR8 ditches let fetcher useFetcher({
routeId: 'routes/api.users.$id' // only routes with actions and/or loaders
})
fetcher.fetch({ params: { id: 'abc' }}) // GET
fetcher.fetch({ method: 'post', params: { id: 'abc' } }) // POST
fetcher.loaderData
fetcher.actionData
Although, a GET would need some sort of revalidation opt out flag for it to behave like a GET |
Beta Was this translation helpful? Give feedback.
-
|
Here's an attempt at reusing |
Beta Was this translation helpful? Give feedback.
-
|
We talked about this in the steering committee meeting today and think we landed a on a nice API that simplifies this a bit and leans into modern react features. These days, React has Here's what we came up with: let [post, updatePost] = useRouteAction({ pattern: '/blog/:slug' })
// ^ ^ both strongly typed to the /blog/:slug route action
let [isPending, startTransition] = useTransition();
let [optimisticPost, setOptimistic] = useOptimistic(post);
// Use your own transition/optimistic state for pending/optimistic UI
startTransition(() => {
setOptimistic({ ...post, text: updatedText });
return updatePost({ text: updatedText }, { method: 'put' });
})This eliminates a bunch of fetcher.* fields that exist today:
BUT WHAT ABOUT let [data, submit, reset] = useRouteAction({ pattern })It's a less-commonly used thing so many folks will never bother to even destructure it off, but it's available for those that do.
These would all still be fetchers under the hood so they'd still get all the cancellation/interruption/revalidation behavior of |
Beta Was this translation helpful? Give feedback.
-
|
You should take a look at my implementation for type safe fetchers First of all - one route has multiple actions (yes!) // route: /some/:slug
export const actions = {
'action-one': (args: ActionArgs<{ data: SomeDataShapeActionOne }>) => {
// returns Promise<ActionOneReturnType> inferred
return clientAPI.POST('/api/some/path', { body: args.data })
},
'action-two': (args: ActionArgs<{ data: SomeDataShapeActionTwo }>) => {
// returns Promise<ActionTwoReturnType> inferred
return clientAPI.POST('/api/some/path/two', { body: args.data })
},
}And i use // Some compoent inside some route
const fetcher = useSubmitFetcher({
path: '/some/:slug', // type safe
action: 'action-one' // type safe
})
fetcher.data // type safe -> ActionOneReturnType | undefined
fetcher.state // type safe -> 'idle' | 'submitting'
// in order to call action (in onClick for example):
fetcher.submit({
data: { /* type safe - only `SomeDataShapeActionOne` is allowed here */ },
paramsToReplace: { slug: 'some-slug' } // type safe, you must provide `slug` param
}
)
// if you want to call action 'action-two' you need another fetcher:
// const fetcher = useSubmitFetcher({
// path: '/some/:slug',
// action: 'action-two'
//})
// usage is the same as above but type of `fetcher.data` is `ActionTwoReturnType | undefined` and etc.It is easier for loaders... const fetcher = useLoader('/some/:slug')
fetcher.data // type safe -> return type of /some/:slug loader | undefined
fetcher.load({
paramsToReplace: { slug: 'some-slug' } // type safe, you must provide `slug` param
}
)I know that it can't be easily generalized for the whole react router project (other APIs and all supported use cases), but for me, it is quite a powerful custom fetchers API that I use a lot and this is really good fit for project i am currently working on. Just decided to share with you for may be inspiration. This custom fetchers API is used a lot in the Evidently OSS project. Real code examples:
Feel free to clone the repo and check it live - it is a fully working project with 100% type safety with no framework mode/codegen types - just vanilla ts types infered from source code (with a bit of limits but still pretty powerful). I created custom wrapper around react router for this before you released full type safety You can check the whole project here https://github.com/evidentlyai/evidently/tree/79c758ff60c8d01ba5f39d34c76b7b8d654a295e/ui/service |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
tl;dr
Background
useFetcherhas no way to be typesafe because thedatachanges based on what you do with it. You can theoretically use the same fetcher instance for both aload()andsubmit()in the same component so unless your route'sactionandloaderhave the same return type, the types will lying to you if you typehint the useFetcher. Example:Here,
fetcher.datawould be the loader's return data but it's typed as if the action will be called.Abstraction
For this to work, the intent of the request needs to be established at fetcher initialization.
However, if this were done to
useFetcher, the methods make less sense:I believe this warrants a new utility.
Proposal
A new,
useRouteHandlerhook that would be configured just enough before use to be typesafe.We should be able to use fetchers under the hood. Something like this. (useMemo's removed for clarity)
This version still has distinct methods for calling an action(
submit) vs a loader(load) but they're mutually exclusive.Considerations
I know some people prefer the distinction at the callsite but it could probably be normalized into a common function who's arg type changes based on how it's configured.
The
fetchfunction could also be namedsubmitsince that makes sense for all verbs considering you cansubmitagetrequest with a fetcher now.searchParams
Kinda goofy since they can be part of the action and also become the payload for GET submissions. I'm not sure the best way to handle them.
It might make sense to separate them out at the callsite(if we want to support them there) and adjust the
bodytype.Having a dedicated place to put searchParams for both requests might make more sense from a consistency standpoint. Also matches better with builtin fetch() since bodies are ignored with GET requests.
Something like this.
Alternative
One alternative would be to try to enhance useFetcher and add
loaderDataandactionDatato its return.However, with this approach i think we'd want typesafey on existing methods
Beta Was this translation helpful? Give feedback.
All reactions