React-select: ScrollLock dist missing window undefined check

Created on 4 Oct 2019  路  8Comments  路  Source: JedWatson/react-select

While attempting to upgrade react-select from version 2.4.4 to the latest 3.0.8 I'm running into issues with server side rendering and the following error:

ErrorDetails : ReferenceError: window is not defined
    at ./node_modules/react-select/dist/base/dist/react-select-cac0a5ae.browser.esm.js (server.min.js:117809:17) -> var canUseDOM = !!(window.document && window.document.createElement);

We use this within a .Net CMS by way of ReactJS.Net so the window object is not available while it is being rendered on the server. The strange thing is that looking at the dist output from the npm package, the check for window being undefined isn't there:
image
However, looking at the src it is:
image

The check did exist in the dist output of the 2.4.4 version so something with the build process may have been changed with version 3.0.0 that could have caused this? Thank you for looking at the issue!

issubug-unconfirmed issureviewed

Most helpful comment

The bundle you use on the server should be built with webpacks target: 'node' to resolve modules from 'main' or 'module' fields and not using client-side specific aliases.

Since you're not running it under nodejs (ReactJS.NET does not provide full nodejs environment) you will get "require is not defined" when trying to use bundle built like this but there is away.

In the server build, keep target: 'web' (default when unspecified) but change resolve.aliasFields to an empty array, like this:

module.export = {
 ...
 resolve: {
    aliasFields: []
  }
}

This way webpack will not use browser specific aliases when resolving react-select module... Ufff... :)

All 8 comments

@swensorm Have you found any solution?

I have a workaround in place for the time being, but it's really not ideal as it renders nothing server side so there's a delay to it appearing in the browser. The exception would be thrown just from the static import, since the offending code isn't part of the component, so I have to dynamically require it.

if (typeof window === 'undefined') {
  return null;
}
const { default: Select, components } = require('react-select'); // eslint-disable-line global-require

The bundle you use on the server should be built with webpacks target: 'node' to resolve modules from 'main' or 'module' fields and not using client-side specific aliases.

Since you're not running it under nodejs (ReactJS.NET does not provide full nodejs environment) you will get "require is not defined" when trying to use bundle built like this but there is away.

In the server build, keep target: 'web' (default when unspecified) but change resolve.aliasFields to an empty array, like this:

module.export = {
 ...
 resolve: {
    aliasFields: []
  }
}

This way webpack will not use browser specific aliases when resolving react-select module... Ufff... :)

I was able to solve a similar issue with "window is not defined" when using SSR with reactjs.net by using your fix @mgrzyb .
Issue was described in ticket https://github.com/marcelltoth/react-select-reborn/issues/8.

While I can confirm that adding aliasFields: [] to the webpack config did indeed fix the drop-down rendering server side, it had the unfortunate side effect of causing over 1 MB of locale data from the Intl package to be included as well.

While I can confirm that adding aliasFields: [] to the webpack config did indeed fix the drop-down rendering server side, it had the unfortunate side effect of causing over 1 MB of locale data from the Intl package to be included as well.

Same :)

Why can't you check if windows is defined on canUseDOM() function?
I'd like to use some of hacks mentioned above but:
1) I can't conditionally render this component because my SSR config is still going through all the code and encounters this canUseDOM method even in case when it's not rendered.
2) I can't modify my webpack config due to project configuration (it's create-react-app based template and ejecting would cause much damage).

In the code, it looks like there is a check for typeof window !== 'undefined'

However, in the compiled version I see:

// node_modules/react-select/dist/Select-9fdb8cd0.browser.esm.js:430s

var canUseDOM = !!(window.document && window.document.createElement);

Manually updating that code fixes the issue. Why does the code in the git repo differ from the built version on NPM?

Potential solution

For those looking for a potential fix, here are there solutions:

  1. Use the older version (v2.4.4)
  2. Add this to your webpack config (鈿狅笍 I do not know all of the potential side effects of this):
  {
    resolve: {
      alias: {
        "react-select": require.resolve(
          "react-select/dist/react-select.cjs.prod.js"
        ),
      },
    },
  }

I think we are going to go with option 1...

UPDATE!

I found the culprit. Looks like preconstruct js is replacing things quietly:

https://github.com/preconstruct/preconstruct/blob/master/packages/cli/src/build/rollup.ts#L137

Was this page helpful?
0 / 5 - 0 ratings

Related issues

MalcolmDwyer picture MalcolmDwyer  路  3Comments

mjuopperi picture mjuopperi  路  3Comments

AchinthaReemal picture AchinthaReemal  路  3Comments

mbonaci picture mbonaci  路  3Comments

steida picture steida  路  3Comments