Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Children Outlet route intercepts Layout action #8923

Closed
D-Rekk opened this issue Feb 28, 2024 · 5 comments · Fixed by remix-run/react-router#12003
Closed

Children Outlet route intercepts Layout action #8923

D-Rekk opened this issue Feb 28, 2024 · 5 comments · Fixed by remix-run/react-router#12003
Labels
awaiting release This issue has been fixed and will be released soon bug Something isn't working feat:routing

Comments

@D-Rekk
Copy link

D-Rekk commented Feb 28, 2024

Reproduction

https://stackblitz.com/edit/remix-run-remix-2up1w8

System Info

System:
    OS: Linux 5.0 undefined
    CPU: (6) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
    Memory: 0 Bytes / 0 Bytes
    Shell: 1.0 - /bin/jsh
  Binaries:
    Node: 18.18.0 - /usr/local/bin/node
    Yarn: 1.22.19 - /usr/local/bin/yarn
    npm: 10.2.3 - /usr/local/bin/npm
    pnpm: 8.14.0 - /usr/local/bin/pnpm
  npmPackages:
    @remix-run/css-bundle: * => 2.7.2 
    @remix-run/dev: * => 2.7.2 
    @remix-run/node: * => 2.7.2 
    @remix-run/react: * => 2.7.2 
    @remix-run/serve: * => 2.7.2

Used Package Manager

npm

Expected Behavior

Button with submit() placed in the Layout route should trigger the action of its corresponding action, independently if the current route is nested or has query params.

Actual Behavior

Once you have submitted an action from an Outlet route and that action appends the search param ?index, when the button in the parent Layout route submits an action, that action will be intercepted by the Outlet route action (app._index.tsx) instead of it being read by the Layout route (app.tsx)

@kiliman
Copy link
Collaborator

kiliman commented Feb 28, 2024

It's not a bug. When you submit, by default it will POST to the the URL the user is currently on. Once you submitted from app._index, the URL changed to /app?index.

This meant when you submitted from your parent layout, it will POST to the app._index action. To ensure you always submit to the current layout action, specify the action prop in options. action: '.' means to POST to current route.

NOTE: useSubmit will cause a navigation to the action URL, just like <Form> submits. If you want to post without navigation then either useFetcher or add navigate: false to options.

https://remix.run/docs/en/main/hooks/use-submit#options

<button
        onClick={() => {
          submit(
            { _action: 'app.tsx' },
            {
              action: '.',
              method: 'POST',
              replace: true,
              encType: 'multipart/form-data',
              navigate: false,
            }
          );
        }}
      >

@D-Rekk
Copy link
Author

D-Rekk commented Feb 28, 2024

I see, setting action: "." is what I expected to be the default behavior.
It would be useful if the docs were a bit more explicative about this. For action options it says: action: The href to submit to. Default is the current route path . But it's the current visited route, not the current file route.

@kiliman
Copy link
Collaborator

kiliman commented Feb 28, 2024

Remix is trying to emulate the default browser behavior. For forms, a missing or empty action attribute will always submit to the current URL in the address bar.

Also, the browser will not submit to a partial URL without a specific action specified. When specifying paths in Remix (React-Router) in <Form action> and <Link to>, it defaults to using route-relative paths. If you want URL-relative paths, then you'll need to set relative='path'

@brophdawg11
Copy link
Contributor

I think this is actually a bug in the useSubmit/fetcher.submit flows.

Remix should by default post to the contextual action - which is usually the action in the same file as the form (or the action in the nearest contextual ancestor route):

export function action () {}

export default function Component() {
  // When no action is specified - this should default to the action for this route
  return <Form method="post">...</Form>
}

This is currently working correctly for <Form>, <Form navigate={false}>, and <fetcher.Form> because they all render a default <form action> attribute value when no action prop is passed and that handles removing the ?index param so we post to the contextual route action.

That doesn't happen for useSubmit/fetcher.submit - but I think it probably should since useSubmit should be essentially interchangeable with <Form>.

export function action () {}

export default function Component() {
  let submit = useSubmit()
  // When no action is specified - this should *also* default to the action for this route
  return <button onClick={() => submit({}, { method: 'post' })}>...</button>
}

@brophdawg11
Copy link
Contributor

This is fixed by remix-run/react-router#12003 and will be available in the next release 👍

@brophdawg11 brophdawg11 added the awaiting release This issue has been fixed and will be released soon label Sep 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
awaiting release This issue has been fixed and will be released soon bug Something isn't working feat:routing
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants