oedotme / generouted

Generated file-based routes for Vite
https://stackblitz.com/github.com/oedotme/generouted/tree/main/explorer
MIT License
1.02k stars 47 forks source link

React Router actions are not working with index routes #131

Closed polarisdigitech closed 10 months ago

polarisdigitech commented 11 months ago

I just converted my react-router-dom project to use generouted only to realise that my actions stopped working. I've seen some examples using loaders as Loader but no example for actions. I tried also using Action but got

Screenshot 2023-10-12 at 14 23 23

Does generouted support react-router actions?

My code example

export function Loader() {
  const session = ...

  if (session) {
    return redirect("/");
  }

  return null;
}

export async function Action({ request }: ActionFunctionArgs) {
  const form = await request.formData();
  return login(Object.fromEntries(form));
}

export default function Login() {
  return (
            <Form
              method="post"
              action={`/login?continueTo=${from}`}
            >
              <div>
                <label htmlFor="username">Username</label>
                <input name="username" required />
              </div>

              <div>
                <label htmlFor="password">Password</label>
                <input type="password" name="password" required />
              </div>

              <button type="submit">Sign In</button>
            </Form>
  );
}
oedotme commented 11 months ago

@polarisdigitech Yes, exported Action should work as well as the Loader.

Here's a working StackBlitz based on your example. The error you're getting is mostly because you're using an action prop for Form component, to a route that doesn't have an Action.

Hope that helps!

polarisdigitech commented 11 months ago

The code example in my issue is exactly what I have in my code. I have the Action and the action on the form.

oedotme commented 11 months ago

@polarisdigitech what's the file name/path you took this code example from?

polarisdigitech commented 10 months ago

Hi @oedotme it's login/index.tsx

oedotme commented 10 months ago

@polarisdigitech I'm not sure if that's an issue with generoutedreact-router somehow ignores the action when the route has index: true with no path, however when a route has a regular path it works as expected.

Please feel free to report that directly to the react-router repo.

polarisdigitech commented 10 months ago

You're right, changing the file to login.tsx fixed it

polarisdigitech commented 10 months ago

@oedotme Which means that posts/[id]/index.tsx like in the stackblitz example cannot have an action?

oedotme commented 10 months ago

@polarisdigitech Yes it seems general to any index: true route.

polarisdigitech commented 10 months ago

That's a buzz kill, I'll try finding a way around it

henrikvilhelmberglund commented 9 months ago

@oedotme Which means that posts/[id]/index.tsx like in the stackblitz example cannot have an action?

It seems like it works if you have posts/[id]?index and have the action in the index. (I could be misunderstanding though)

oedotme commented 9 months ago

@henrikvilhelmberglund I haven't tried that, but as I mentioned, that's a bug in react-router-dom

I think it's expected that actions should work with index: true routes whether it's optional or not.

henrikvilhelmberglund commented 9 months ago

@henrikvilhelmberglund I haven't tried that, but as I mentioned, that's a bug in react-router-dom

I think it's expected that actions should work with index: true routes whether it's optional or not.

The problem is that apparently React Router expects actions to be in layouts by default so if you have posts/[id]/index.tsx and posts/[id]/_layout.tsx an action with action={`/login/index`} will match both which is kind of a problem. Because of this they decided that /login/index will match the parent route (aka layout I think) by default and index only if you include ?index at the end as in action={`/login/?index`} or action={`/login?index`}.

This is only true if you specify the action in the form, if you don't and the action is in an index route React Router will just add ?index automatically so it uses the action in the index route.

No idea if any of this made sense but the docs can probably explain this better than me! https://reactrouter.com/en/main/guides/index-search-param

edit. backticks 😱 also mixed up id and login but hopefully you get the point.

oedotme commented 9 months ago

@henrikvilhelmberglund Thanks for pointing that out! I wan't aware of this behavior.

@polarisdigitech This now should work with your initial example by adding the index param at the action prop url:

export function Loader() {
  const session = ...

  if (session) {
    return redirect("/");
  }

  return null;
}

export async function Action({ request }: ActionFunctionArgs) {
  const form = await request.formData();
  return login(Object.fromEntries(form));
}

export default function Login() {
  return (
            <Form
              method="post"
              // before
              // action={`/login?continueTo=${from}`}
              // after — adding the `index` search param
              action={`/login?index&continueTo=${from}`}
            >
              <div>
                <label htmlFor="username">Username</label>
                <input name="username" required />
              </div>

              <div>
                <label htmlFor="password">Password</label>
                <input type="password" name="password" required />
              </div>

              <button type="submit">Sign In</button>
            </Form>
  );
}

Here's the updated working example: https://stackblitz.com/edit/github-yh2whq?file=src/pages/login/index.tsx:L27

Thanks again @henrikvilhelmberglund!