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

fix: Import React.cache only when needed #923

Merged
merged 1 commit into from
Feb 21, 2025
Merged

Conversation

franky47
Copy link
Member

@franky47 franky47 commented Feb 21, 2025

Having import { cache } from 'react' caused issues when using React 18 or 19 GA, because the cache function is only exported in canary builds (which the Next.js app router uses internally, regardless of what's in your app's package.json).

This meant importing from 'nuqs/server' caused an import error when done from non app-router code, like the pages router, an API route definition, or a non-Next.js framework, which would fallback to the version of React defined in the package.json (likely a stable one).

Changing the import to import * as React from 'react' is what is being highlighted in the React docs themselves, and allows to only call the cache function when actually creating a cache object.

Closes #804, and supersedes #805.

Having `import { cache } from 'react'` caused issues when using
React 18 or 19 GA, because the `cache` function is only exported
in canary builds (which the Next.js app router uses internally,
regardless of what's in your app's package.json).

This meant importing from `'nuqs/server'` caused an import error
when done from non app-router code, like the pages router, or
API route definitions, which would fallback to the version of React
defined in the package.json (and likely a stable one).

Changing the import to `import * as React from 'react'` is what is
being highlighted in the React docs themselves, and allows to only
call the cache function when actually creating a cache object.

Closes #804, and supersedes #805.
@franky47 franky47 added this to the 🪵 Backlog milestone Feb 21, 2025
Copy link

vercel bot commented Feb 21, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
nuqs ✅ Ready (Inspect) Visit Preview 💬 Add feedback Feb 21, 2025 4:22pm

Copy link

pkg-pr-new bot commented Feb 21, 2025

pnpm add https://pkg.pr.new/nuqs@923

commit: 12ec159

@franky47 franky47 enabled auto-merge (squash) February 21, 2025 16:24
@franky47 franky47 merged commit 0c955ce into next Feb 21, 2025
29 checks passed
@franky47 franky47 deleted the fix/804-react-cache-import branch February 21, 2025 16:25
Copy link

github-actions bot commented Mar 6, 2025

🎉 This PR is included in version 2.4.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

@rik-iso
Copy link

rik-iso commented Mar 6, 2025

web:build | ../../node_modules/.pnpm/nuqs@2.4.1_next@14.2.23_@babel+core@7.24.6_@opentelemetry+api@1.9.0_react-dom@18.3.1_react@18_254swa4g7i3oqurqrbun7hl3xa/node_modules/nuqs/dist/server.js
web:build | Attempted import error: 'cache' is not exported from 'react' (imported as 'React').

web:build | Import trace for requested module:
web:build | ../../node_modules/.pnpm/nuqs@2.4.1_next@14.2.23_@babel+core@7.24.6_@opentelemetry+api@1.9.0_react-dom@18.3.1_react@18_254swa4g7i3oqurqrbun7hl3xa/node_modules/nuqs/dist/server.js
web:build | ./app/features/search/(sections)/products/hooks/filters/getProductSearchFilterParsers.ts
web:build | ./app/features/search/(sections)/products/hooks/filters/useProductSearchQueryStates.ts
web:build | ./components/page/marketplace/MarketplaceProducts.tsx

still seeing this issue on 2.4.1

@franky47
Copy link
Member Author

franky47 commented Mar 6, 2025

@rik-iso what's your setup?

@franky47 franky47 removed this from the 🚀 Shipping next milestone Mar 6, 2025
@rik-iso
Copy link

rik-iso commented Mar 6, 2025

This might be operator error but I'm confused how to fix it.

The specific file getProductSearchFilterParsers is importing:

import { parseAsArrayOf, parseAsInteger, parseAsString, parseAsStringEnum, parseAsStringLiteral } from 'nuqs/server'

In the above crash, it's being used in a pages router page, but it also needs to be used in an app router page (we're mid-migration). Should I be importing it differently?

@rik-iso
Copy link

rik-iso commented Mar 6, 2025

(The crash actually only happens on next build and not in my dev server)

@franky47
Copy link
Member Author

franky47 commented Mar 6, 2025

Are you using the cache feature (createSearchParamsCache) anywhere at all?

I'll try and reach out to the Next.js team to see if they have ideas for this kind of use-case, and I'll try and reproduce on my end in the mean time.

@rik-iso
Copy link

rik-iso commented Mar 6, 2025

Yeah I am using that cache on this property but only in the app router pages - the cache shouldn't be being used in the pages router code path.

@rik-iso
Copy link

rik-iso commented Mar 6, 2025

I'm going to see if I can refactor out the use of the cache as I think it might be mostly redundant and see if that stops the error for now.

@franky47
Copy link
Member Author

franky47 commented Mar 6, 2025

If you don't need the no-prop-drilling property of the cache, you can use a loader instead.

@rik-iso
Copy link

rik-iso commented Mar 6, 2025

Possibly found the culprit - for some reason I am doing

export type SearchQueryState = ReturnType<typeof searchQueryStateCache.all>
export const searchQueryStateCache = createSearchParamsCache(getProductSearchFilterParsers(SEARCH_DEFAULT_PARSERS))

then I am only importing SearchQueryState using import type into a pages router page... but maybe that's enough to break the build (but not dev server).

Cleaning this up to use inferParserType... it's code I wrote in early nuqs v1 days so maybe that didn't exist then or I didn't know about it 🙃 .

@rik-iso
Copy link

rik-iso commented Mar 6, 2025

nevermind, that didn't solve it sadly.

@franky47
Copy link
Member Author

franky47 commented Mar 6, 2025

Could you paste the result from running next info please?

While trying to reproduce this in the e2e environment, I managed to get the cache to compile (and partially run) in the pages router, which is definitely not supposed to happen 🙃 (ie: I can't get it to fail on next build).

@rik-iso
Copy link

rik-iso commented Mar 6, 2025

$ pnpm next info

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 24.3.0: Thu Jan  2 20:24:23 PST 2025; root:xnu-11215.81.4~3/RELEASE_ARM64_T6020
  Available memory (MB): 32768
  Available CPU cores: 12
Binaries:
  Node: 22.13.1
  npm: 9.6.7
  Yarn: N/A
  pnpm: 9.15.4
Relevant Packages:
  next: 14.2.23 // An outdated version detected (latest is 15.2.1), upgrade is highly recommended!
  eslint-config-next: N/A
  react: 18.3.1
  react-dom: 18.3.1
  typescript: 5.7.3
Next.js Config:
  output: N/A
 ⚠ An outdated version detected (latest is 15.2.1), upgrade is highly recommended!
   Please try the latest canary version (`npm install next@canary`) to confirm the issue still exists before creating a new issue.
   Read more - https://nextjs.org/docs/messages/opening-an-issue

@rik-iso
Copy link

rik-iso commented Mar 6, 2025

Please don't spend too many cycles on this right now if it's not trivial to reproduce (unless someone else reports the same issue of course) - I've worked around it. That said I'm happy to try and help repro it as much as possible if it helps!

I'm working around it for now by making a copy of the file that depends on nuqs/server and using nuqs in it, then prop drilling the right version of the method down depending if it's in app router or pages router 🙃 . It's highly possible there's something weird in our codebase (which is a bit of a frankenstein's monster of app and pages router at this point).

@franky47
Copy link
Member Author

franky47 commented Mar 6, 2025

Ahh I get the error by reproducing your exact setup (matching Node.js, React & Next.js versions). So updating some of those might solve the issue. My bet is on React, I'll see if bumping just that one to 19 solves it.

Edit: bingo, switching to React 19 did solve the issue. Which is weird because 19 doesn't export the cache, it's still in canary..

I'd like to get to the bottom of it just so that I can advise others that encounter the same issue.

@rik-iso
Copy link

rik-iso commented Mar 6, 2025

Just react? react-dom too?

I'm concerned that a React upgrade may be a herculean task on this old repo but iirc for the pages router it will stay effectively stuck to React 18?

but good to know. we're luckily burning the world and building a new app soon on Next 15/R19 so not inclined to go too deep on this if nuqs itself is fine (phew) and I have a workaround here...

Thanks so much for looking into this and for nuqs. Looking forward to adding nuqs to our new app too!

@franky47
Copy link
Member Author

franky47 commented Mar 6, 2025

It's only in the react package, though I keep both in sync with pnpm catalogs (it's in a monorepo too, getting mismatching versions across packages causes weird issues).

TIL that react@19.0.0 does indeed export the cache function, even though the docs say it's only in Canary.

@rik-iso
Copy link

rik-iso commented Mar 6, 2025

Oh I was looking for something like pnpm catalogs just the other day, thanks for showing me that! Time to get pnpm upgraded to v10.

@franky47
Copy link
Member Author

franky47 commented Mar 6, 2025

You're welcome! Just watch out for postinstall scripts not running by default in v10.

This can help (Next.js needs sharp & swc):

nuqs/package.json

Lines 35 to 43 in 0910b2d

"pnpm": {
"onlyBuiltDependencies": [
"@sentry/cli",
"@swc/core",
"cypress",
"esbuild",
"sharp"
]
},

@franky47
Copy link
Member Author

franky47 commented Mar 7, 2025

Thanks for the sponsorship! 💖

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

Successfully merging this pull request may close these issues.

nuqs/server requires react
2 participants