react-use icon indicating copy to clipboard operation
react-use copied to clipboard

react-use cannot be imported in Node.js

Open ocavue opened this issue 3 years ago • 13 comments

What is the current behavior?

I can't use ESM syntax to import react-use in Node.js. I found this issue when I try to run some unit tests on Node.js.

The reason for this issue is that the following package.json is not supported by Node.js. There is a detailed explanation https://github.com/sheremet-va/dual-packaging

{
  "name": "react-use",
  "main": "lib/index.js",
  "module": "esm/index.js",
  ...
}

There are some simple methods to fix this issue. More explanation in the link above.

  1. Put a package.json under esm/, with the content {"type": "module"};
  2. Or, rename all .js file under esm/ to .mjs;
  3. Or. rename all .js file under lib/ to .cjs.

Steps to reproduce it and if possible a minimal demo of the problem. Your bug will get fixed much faster if we can run your code and it doesn't have extra dependencies other than react-use. Paste the link to your JSFiddle or CodeSandbox example below:

bash-3.2$ pnpm add react-use
dependencies:
+ react-use 17.4.0
bash-3.2$
bash-3.2$ echo 'import {useDefault} from "react-use"' > a.mjs
bash-3.2$
bash-3.2$ node a.mjs
file:///private/tmp/a.mjs:1
import {useDefault} from "react-use"
        ^^^^^^^^^^
SyntaxError: Named export 'useDefault' not found. The requested module 'react-use' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from 'react-use';
const {useDefault} = pkg;

    at ModuleJob._instantiate (node:internal/modules/esm/module_job:128:21)
    at async ModuleJob.run (node:internal/modules/esm/module_job:194:5)
    at async Promise.all (index 0)
    at async ESMLoader.import (node:internal/modules/esm/loader:385:24)
    at async loadESM (node:internal/process/esm_loader:88:5)
    at async handleMainPromise (node:internal/modules/run_main:61:12)

What is the expected behavior?

react-use can be imported in a .mjs file with ESM syntax.

A little about versions:

  • OS: macOS
  • Browser (vendor and version): N/A
  • React: N/A
  • Node: 16.15
  • react-use: 17.4.0
  • Did this worked in the previous package version?: No

ocavue avatar Jun 20 '22 08:06 ocavue

bash-3.2$ pnpm add react-use dependencies:

  • react-use 17.4.0 bash-3.2$ bash-3.2$ echo 'import {useDefault} from "react-use"' > a.mjs bash-3.2$ bash-3.2$ node a.mjs file:///private/tmp/a.mjs:1 import {useDefault} from "react-use" ^^^^^^^^^^ SyntaxError: Named export 'useDefault' not found. The requested module 'react-use' is a CommonJS module, which may not support all module.exports as named exports. CommonJS modules can always be imported via the default export, for example using:

import pkg from 'react-use'; const {useDefault} = pkg;

at ModuleJob._instantiate (node:internal/modules/esm/module_job:128:21)
at async ModuleJob.run (node:internal/modules/esm/module_job:194:5)
at async Promise.all (index 0)
at async ESMLoader.import (node:internal/modules/esm/loader:385:24)
at async loadESM (node:internal/process/esm_loader:88:5)
at async handleMainPromise (node:internal/modules/run_main:61:12)

I also faced this problem. Is there a temporary solution?

aamree avatar Sep 07 '22 06:09 aamree

I use a setup where react-use is imported by both a node module (node 16) and nextjs - node cannot use named exports reliably for CJS and if i use the default export format, then nextjs' build fails saying that there isn't a default export (because there isn't, seeing how it's node's interop that adds it). Using a namespace import leads to Node not recognizing all default re-exports.

Direct file import such as

import useUpdateEffect from 'react-use/lib/useUpdateEffect.js';

leads to

{ default: [Function: useUpdateEffect] }

Needless to say, ESM CJS interop is a mess...

nemanja-tosic avatar Oct 05 '22 12:10 nemanja-tosic

I'm getting the same issue. I'm updating my app to react@18 and next@13, which automatically supports properly-packaged esm packages. However, as stated by the OP, a .mjs file can't import a .js file even though its contents are ESM. Is it possible to export the esm version as .mjs files?

edit: It seems that it's possible to add a package.json with { type: module } in the dist/esm/ directory, but I'm not sure how that would affect existing projects that are using the current esm exports.

kaisermann avatar Jul 10 '23 11:07 kaisermann

This seems to happen to me to using Remix. Everything should be ESM, but I still get server-side errors trying to import the library.

I'll have to move over to some other hooks library.

Mange avatar Oct 05 '23 21:10 Mange

Just found this issue. In case it helps somebody else, here's my workaround until the underlying problem is resolved:

import useKeyLib from "react-use/lib/useKey.js";
const useKey = useKeyLib.default;

msurdi avatar Nov 18 '23 15:11 msurdi

I have the same issue. I made a quick reproduction with remix, using the create-remix, then I added react-use. Any recommended import is failing to compile. (repro: https://github.com/ThibautMarechal/react-use-in-remix)

ThibautMarechal avatar Mar 04 '24 14:03 ThibautMarechal

I am also experiencing the issue when using Remix. This import error is a real downside to using this awesome library. I hope there will be a fix soon.

folushooladipo avatar Mar 19 '24 12:03 folushooladipo

Also, for anyone using TypeScript and Remix, here's another possible workaround that extends @msurdi 's own:

  • create a file in your project into which you'll import the hooks you need from react-use, patch their default export issues and then export the hooks for all your components to use.
  • Patch the hooks like this:
import useKeyPressEventLib from 'react-use/lib/useKeyPressEvent'
export const useKeyPressEvent = (useKeyPressEventLib as any).default as typeof useKeyPressEventLib

folushooladipo avatar Mar 19 '24 12:03 folushooladipo

For Vite-based projects (& Remix), this configuration should also solve the issue:

// vite.config.ts
import { defineConfig } from "vite";

export default defineConfig((env) => {
  return {
    // […]
    ssr: {
      noExternal: ["react-use"],
    },
    optimizeDeps: {
      include: ["react-use"],
    },
  };
});

real34 avatar Apr 09 '24 10:04 real34

For reference: https://publint.dev/[email protected]

tizmagik avatar May 21 '24 13:05 tizmagik