vercel / next.js

The React Framework
https://nextjs.org
MIT License
125.08k stars 26.72k forks source link

Module imported in different files re-evaluated #49309

Open jesinmat opened 1 year ago

jesinmat commented 1 year ago

Verify canary release

Provide environment information

Operating System:
      Platform: linux
      Arch: x64
      Version: #1 SMP Wed Mar 2 00:30:59 UTC 2022
    Binaries:
      Node: 17.9.0
      npm: 8.19.3
      Yarn: N/A
      pnpm: N/A
    Relevant packages:
      next: 13.4.1-canary.2
      eslint-config-next: 13.4.1
      react: 18.2.0
      react-dom: 18.2.0

Which area(s) of Next.js are affected? (leave empty if unsure)

No response

Link to the code that reproduces this issue

https://github.com/jesinmat/next-reimport

To Reproduce

Build and run the project. Open the app in your browser. You should see the following:

Current ID: 1
<Change ID>

In the server log, you will see that a cache has been created (by importing lib/cache.ts):

Creating a cache
Loading data from database
Returning data 1

Now, click on the "Change ID" button. You should see the following in the server log:

Creating a cache
API request received
Setting data to 2

Now refresh the page. Client will still see 1 as the ID as there are now two caches, since it was imported twice and evaluated upon each import. This should not happen.

Describe the Bug

According to ECMAScript specification, importing a module from different places should only evaluate it once. I wanted to use this fact to create a "cache", but it seems like the imported file is re-evaluated during imports from different files, which makes it impossible to use as a singleton.

The file in question is located at lib/cache.ts and it's imported in app/page.tsx and pages/api/change/index.ts. Importing from these two files causes Cache constructor to be called twice, effectively exporting two completely separate instances of the class.

Expected Behavior

File lib/cache.ts is only evaluated once and the result is exported and shared between all places.

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

niemal commented 1 year ago

Exactly what I am losing my hair on. I tried attaching my instance singleton on the global variable, didn't do it. @jesinmat did you find a work-around or did you just abandon?

jesinmat commented 1 year ago

I did not manage to find a work-around, this is an existing issue that's still bothering me.

niemal commented 1 year ago

I did not manage to find a work-around, this is an existing issue that's still bothering me.

This is how I am exporting my instance if you are interested in trying:

const globalRoomsHandler = global as typeof global & {
  roomsHandler?: RoomsHandler;
};

if (!globalRoomsHandler.roomsHandler) {
  globalRoomsHandler.roomsHandler = new RoomsHandler();
}

export const roomsHandler = globalRoomsHandler.roomsHandler;
CarlosBalladares commented 1 year ago

im having the same issue on 13.4.9. The workaround doesn't seem to work

CarlosBalladares commented 1 year ago

Actually it doesn't work when running dev, however when you build and start it it doesnt get reevaluated (with the workaround)

MangoMarcus commented 5 months ago

This issue is getting on for a year old, is there any update for this?

I've noticed modules being executed multiple times both during dev and during the build step.

Eg. if I have a module /src/lib/module.ts which includes a console.log() at the module root (not in an export), and that module is imported in many places throughout my app, the log prints multiple times. Not as many times as the module is imported, but still a handful.

I'd expect it to only print once during the build step, unless I'm missing something?

It's breaking my caching solution in the app router at the moment

etrepum commented 4 months ago

This can also break packages such as yjs and lexical which expect their modules to be evaluated at most once and rely on instanceof checks

saisantosh commented 4 months ago

I am facing the same issue on 14.3.2 . Has anyone found a work around ?

Edit by maintainer bot: Comment was automatically minimized because it was considered unhelpful. (If you think this was by mistake, let us know). Please only comment if it adds context to the issue. If you want to express that you have the same problem, use the upvote 👍 on the issue description or subscribe to the issue for updates. Thanks!

dedesite commented 3 months ago

So I'm not crazy, module are re-evaluated across import ! I spend hours trying to implement a singleton mecanism with Next.js without success (at least in dev mode) ! I want to store in-memory data related to websockets connections and be able to share them across modules and to want to setup something like redis for that...

EDIT : using global variable like done in the above workaround did the trick for me. Thanks @bsorrentino

DanqingLuan commented 3 months ago

It seems that I am not the only one who has encountered such a problem. I created and exported a Session class to save global Session information and perform related operations on it through a Server action class. However, I found that when using Server action in different Pages, two different copies of the Session class were created. This is crazy. FUCK Next.js!

nigelwtf commented 2 months ago

It seems wild to me that Next.js can silently modify behaviour defined in TC39 and ignore an issue the community has raised for over a year (in-fact it's worse—the organisation I work for is a paying Vercel customer, so we're essentially paying for broken or outright bad software).

The fact that so much of React's future has been entrusted to Vercel does not bode well for anybody.

BitOfaLegend commented 1 month ago

I am running into this problem as well on the current latest release 14.2.5