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

<Link> tag reloads page instead of navigating #8791

Open
Edran0111 opened this issue Feb 19, 2024 · 11 comments · May be fixed by #8932
Open

<Link> tag reloads page instead of navigating #8791

Edran0111 opened this issue Feb 19, 2024 · 11 comments · May be fixed by #8932

Comments

@Edran0111
Copy link

Edran0111 commented Feb 19, 2024

Reproduction

https://stackblitz.com/~/github.com/Edran0111/debug-falc-on
https://github.com/Edran0111/debug-falc-on

System Info

System:
    OS: Windows 11 10.0.22631
    CPU: (8) x64 Intel(R) Core(TM) i7-10510U CPU @ 1.80GHz
    Memory: 2.33 GB / 15.85 GB
  Binaries:
    Node: 20.11.0 - C:\Program Files\nodejs\node.EXE
    Yarn: 1.22.21 - ~\AppData\Roaming\npm\yarn.CMD
    npm: 10.4.0 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Edge: Chromium (121.0.2277.112)
    Internet Explorer: 11.0.22621.1
  npmPackages:
    @remix-run/css-bundle: ^2.6.0 => 2.6.0 
    @remix-run/dev: ^2.6.0 => 2.6.0 
    @remix-run/eslint-config: ^2.6.0 => 2.6.0 
    @remix-run/node: ^2.6.0 => 2.6.0 
    @remix-run/react: ^2.6.0 => 2.6.0 
    @remix-run/serve: ^2.6.0 => 2.6.0

Used Package Manager

npm

Expected Behavior

I'm encountering a navigation issue between pages in my Remix application. When clicking on the "Mon compte" button(in the navbar), which is supposed to redirect to /login, the current page simply reloads instead of navigating to the intended route. Interestingly, all other components in my application (such as /a-propos, /a-propos#equipe, and /contact) work as expected. The issue is specific to the "Mon compte" button that points to /login.

I can access the /login page directly by entering the URL in the browser, and from there, navigation to other links like /oubli-mdp, /register, and others in the navbar works without any issues. However, I cannot navigate to the /login page from any other page outside of it.

Additionally, when I'm on the /login, /register, or /oubli-mdp pages, the Remix DevTools do not appear, even when appending "?rdt=true" to the URL. Strangely, the only workaround I've found is to remove the ActionFunction from login.tsx, which allows the navigation to work, but this is obviously not a viable solution.

Although the remix dev tools says one or two times that there is a hydration error out of 10 refresh, the message that is send does not help me with the correction of the problem.
image
This is what happens when i'm on the _index page, i click once on the button of "Mon compte", the loader function is triggered but I still go back on the _index page

As I'm relatively new to Remix and might be missing something, I'm unable to identify the root cause of this issue. I would appreciate any guidance or suggestions on how to resolve this without having to delete the entire ActionFunction.

Actual Behavior

When I click on any button that redirects to /login, the page loads correctly without any issues, and it does not reload the page from where I initiated the click.

PS: I encountered a similar issue previously on my _index page, where I had a simple script for counting characters and a button to submit text for translation. An ActionFunction was set up in root.tsx to handle a switch case, which, for the time being, only included a logout case. The presence of the ActionFunction interfered with the character counting and the form submission on the _index page. The issue has been resolved by relocating the ActionFunction to a different route.

@michaelgainyo
Copy link

I'm also having this issue, both Link and NavLink. While trying to debug, i noticed that it works when there is no fetch call in the loader; which i solved previously by using node-fetch instead for api calls. However, i upgraded to v2.6.0 and node-fetch kinda breaks the app now

@jansedlon
Copy link

jansedlon commented Feb 26, 2024

Just migrated our production app to vite and this started happening. Also happens when redirecting using navigate

@Edran0111
Copy link
Author

Okay maybe you can check by yourselves but I think the problems comes from "process.env" lines... I have a file in which I initialized the firebase app but I wanted privacy so all the keys given by the firebase console, i hid them in the .env file.
The config looked like this:

  authDomain: process.env.FIREBASE_AUTH_DOMAIN,
  projectId: process.env.FIREBASE_PROJECT_ID,
  storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.FIREBASE_APP_ID,
  measurementId: process.env.FIREBASE_MEASUREMENT_ID

Now that i've replaced those lines with the actual firebaseConfig hardcode parameters(basic configuration given by the firebase console), it works well for now in local and in preview mode deployment mode on Vercel.

Please let me know if erasing these similar lines of code worked for you.

@brophdawg11
Copy link
Contributor

This usually comes from errors trying to load the JS for the destination route. Your stackblitz doesn't run because it needs an API key though:

Error: Firebase: Error (auth/invalid-api-key).

I would try to use chrome dev tools or just edit the source in node_modules directly to log the error here to debug further:

window.location.reload();

@jansedlon
Copy link

jansedlon commented Feb 28, 2024

@brophdawg11 In my case it's trying to load an asset with ?client-route=1 for some reason. Interestingly, the file in the screenshot exists when i enter localhost:3000/assets/{assetFile}
image

Then it fails with no error :))

image

But this happens only in production build, not local development

@jansedlon
Copy link

Well, found it :)) In my case, my colleague used Prisma enum in a route component. Is there a way to warn about this?

@brophdawg11
Copy link
Contributor

Yes and no :)

We can warn (and have a PR open to do so in #8932) but the issue is that Remix automatically reloads the page for you so that console is immediately lost if you don't have Preserve log enabled. So that gets us closer, but still not an ideal state.

I think this is more prevalent when using vite since previously I believe these types of module errors would have failed during the esbuild build. But now that vite is "building" on demand in dev they don't surface until you navigate. Maybe there's a way to determine if it's a vite error versus a 404. Or maybe in dev mode we don't reload automatically and just log the console error or something.

@Apsysikal
Copy link

I'm still experiencing this issue in Remix 2.8.1.
I've tried it in Chrome to maybe surface some errors via Preserve Log but the only thing showing up is:
Navigated to http://localhost:3000/admin/dinners

My code for navigating looks like this:

<span className="flex gap-2">
  <Button variant="ghost" asChild>
    <Link to={`${id}/signups`}>View Signups</Link>
  </Button>

  <Button variant="secondary" asChild>
    <Link to={`${id}/edit`}>Edit</Link>
  </Button>

  <deleteFetcher.Form method="POST" action={`${id}/delete`}>
    <Button type="submit" variant="destructive" disabled={isDeleting}>
      {isDeleting ? "Deleting..." : "Delete"}
    </Button>
  </deleteFetcher.Form>
</span>

That's the route I'm trying to link to: https://github.com/Apsysikal/mokupona/blob/dep/vite/app/routes/admin.dinners.%24dinnerId_.edit.tsx

Any help is appreciated.

P.S. after building and serving the app it does navigate. So it seems to be a dev issue.

@Apsysikal
Copy link

Apsysikal commented Mar 13, 2024

For anyone coming after this. The error for me was that i had typescript type ending up on the client. My zod schema contained:

cover: z
  .union([
    z.instanceof(File, { message: "You must select a file" }),
    z.instanceof(NodeOnDiskFile, { message: "You must select a file" }),
  ])
  .instanceof(File, { message: "You must select a file" })
  .refine((file) => {
    return file.size !== 0;
  }, "You must select a file")
  .refine((file) => {
    return file.size <= 1024 * 1024 * 3;
  }, "File cannot be greater than 3MB"),

and because conform uses the schema to also validate client-side it ended up on the client generating the problem mentioned above. Also the message variable process not found... only surfaced when navigating to the url directly (via the address bar).

@diurivj
Copy link

diurivj commented Mar 17, 2024

I experienced something similar. I am using drizzle, zod and shadcnui too so I thought it was related to these packages since you were using these too. But in my case it was something related a loader route that it was not returning nothing if it was failing:

import { db } from '~/utils/db.server';
import { parseWithZod } from '@conform-to/zod';
import { type ActionFunctionArgs, json, redirect } from '@remix-run/node';
import { InsertExpenseSchema, expenses } from 'drizzle/schema';

export async function action({ request }: ActionFunctionArgs) {
  const formData = await request.formData();
  const submission = parseWithZod(formData, {
    schema: InsertExpenseSchema
  });

  if (submission.status !== 'success') {
    return json(
      { result: submission.reply(), expenseId: null },
      { status: submission.status === 'error' ? 400 : 200 }
    );
  }

  let expense;
  try {
    const [insertedExpense] = await db
      .insert(expenses)
      .values(submission.value)
      .returning({ id: expenses.id });

    expense = insertedExpense.id;
  } catch (error) {
    console.error(error);
  }

  return json({ result: submission.reply(), expenseId: insertedExpense.id  }, { status: 201 });
}

export function loader() {
  return redirect('/monthlies');
}

Even tho I was not hitting up this route the error was breaking the app, getting the link refresh behavior.
I also migrated to vite recently, maybe with the previous bundler it would throw the error, the good old one that says something like: a loader didn't return nothing, please return something.

Maybe there is something wrong with the vite plugin that is not showing these kind of silent errors that were not silent with the previous bundler.

@Apsysikal
Copy link

For me it didn't seem to be anything related to that. In the end i had to go further with separating the two validation schemas.

// event-validation.client.ts
import { z } from "zod";

export const ClientEventSchema = z.object({
  title: z.string({ required_error: "Title is required" }).trim(),
  description: z.string({ required_error: "Description is required" }).trim(),
  date: z.coerce.date({ required_error: "Date is required" }),
  slots: z
    .number({ required_error: "Slots is required" })
    .min(0, "Slots cannot be less than 0")
    .int(),
  price: z
    .number({ required_error: "Price is required" })
    .min(0, "Price cannot be less than 0")
    .int(),
  cover: z
    .instanceof(File, { message: "You must select a file" })
    .refine((file) => {
      return file.size !== 0;
    }, "You must select a file")
    .refine((file) => {
      return file.size <= 1024 * 1024 * 3;
    }, "File cannot be greater than 3MB"),
  addressId: z.string({ required_error: "Address is required" }).trim(),
});
// event-validation.server.ts
import { NodeOnDiskFile } from "@remix-run/node";
import { z } from "zod";

export const ServerEventSchema = z.object({
  title: z.string({ required_error: "Title is required" }).trim(),
  description: z.string({ required_error: "Description is required" }).trim(),
  date: z.coerce.date({ required_error: "Date is required" }),
  slots: z
    .number({ required_error: "Slots is required" })
    .min(0, "Slots cannot be less than 0")
    .int(),
  price: z
    .number({ required_error: "Price is required" })
    .min(0, "Price cannot be less than 0")
    .int(),
  cover: z
    .instanceof(NodeOnDiskFile, { message: "You must select a file" })
    .refine((file) => {
      return file.size !== 0;
    }, "You must select a file")
    .refine((file) => {
      return file.size <= 1024 * 1024 * 3;
    }, "File cannot be greater than 3MB"),
  addressId: z.string({ required_error: "Address is required" }).trim(),
});

This was because the uploaded file is an instance of NodeOnDiskFile in the remix action and an instance of File on the client.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants