Replies: 2 comments 10 replies
-
Have you tried |
Beta Was this translation helpful? Give feedback.
5 replies
-
add a form field with JavaScript instead? |
Beta Was this translation helpful? Give feedback.
5 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Hi Remix friends,
A question I've seen pop up several times in Discord (including asked by myself!) is "how do I submit a form of a child resource on my parent reosurce page without causing a navigation?" The answer is of course
useFetcher
, and the useFetcher docs include an example of how to do exactly this via a resource route.As noted in the docs, if your resource route only returns json or a status code, your page will no longer work with javascript disabled because the form will cause a whole navigation to the resource route, which doesn't render anything. The docs currently propose exporting a default component from your resource route that will only be seen when js is disabled. While this works, it feels like a smell to me. 1. It's an extra component to write and maintain that will very rarely be used, and 2. there isn't always a component that makes sense to export from a resource route.
For example, in my app, I have a
Note
model that is polymorphic across many different models. E.g. aVendor
has manyNotes
, as does aProduct
and aCustomer
. I have routes for/vendor/$id
,/product/$id
, and/customer/$id
, which each render (among other things) their list of notes, but I do not have a standalone notes UI as they can't really exist on their own. Each of those pages that has a list of notes has UI for editing, deleting, and starring those notes, so I've created a resource route for each action:PUT /api/notes/$id
,DELETE /api/notes/$id
,PUT /api/notes/star
, and then I invoke those actions usinguseFetcher
from the parent pages.There's nothing I could export default from
/api/notes/star
s to render that would make sense, and as long as javascript is enabled and we're using useFetcher, I don't want to trigger a navigation since these are "child resources" on the "parent" route.So I propose two options that will allow developers to have resource routes with no default export but that will still work with
useFetcher().Form
with javascript disabled:Solutions
useFetcher()
automatically set a header when creating itsfetch
request that says "yes, this request is originating from javascript". Something along the lines ofX-Remix-Fetcher: true
. This would allow the developer to check the header in their resource route actions and conditionally return eitherjson()
orredirect()
based on if the request is a js fetch request or a full HTTP navigation. For example:let fetcher = useFetcher({ followRedirects: false })
. This way, our resource route actions can just always return a redirect without having to know anything about how the request is originating, and the client controls whether the page navigates to that redirect or not. We could also possibly make this the default behavior and not be an option, but to be honest, I think I would personally find that more surprising than a fetcher following an explicitly-returned redirect.Personally I think I prefer option 2; it requires setting the option every time you create a fetcher, but option 1 requires adding conditional logic to every resource route. More importantly, option 2 leaves the client in control of when it should navigate vs when it should not without having to share details about the user's browser to the server.
If people think either of these are a good idea, I'll be happy to open up a corresponding PR! Thanks in advance.
Beta Was this translation helpful? Give feedback.
All reactions