React-rails: 2.5.0 ReferenceError: window is not defined when prerender: true

Created on 12 May 2019  Â·  16Comments  Â·  Source: reactjs/react-rails

Steps to reproduce

  • Upgrade to webpack and webpacker 4, react-rails and react_ujs 2.5.0
  • Run local dev server and try to render a page with SSR components

Expected behavior

Should render the component

Actual behavior

Reference error at runtime

System configuration

Webpacker version: 4.0.2
React-Rails version: 2.5.0
React_UJS version: 2.5.0
Rails version: 5.2.3
Ruby version: 2.6.3


Framework trace:

execjs (2.7.0) lib/execjs/ruby_racer_runtime.rb:15:in `rescue in block in initialize'
execjs (2.7.0) lib/execjs/ruby_racer_runtime.rb:12:in `block in initialize'
execjs (2.7.0) lib/execjs/ruby_racer_runtime.rb:75:in `block in lock'
execjs (2.7.0) lib/execjs/ruby_racer_runtime.rb:73:in `Locker'
execjs (2.7.0) lib/execjs/ruby_racer_runtime.rb:73:in `lock'
execjs (2.7.0) lib/execjs/ruby_racer_runtime.rb:9:in `initialize'
execjs (2.7.0) lib/execjs/runtime.rb:57:in `new'
execjs (2.7.0) lib/execjs/runtime.rb:57:in `compile'
execjs (2.7.0) lib/execjs/module.rb:27:in `compile'
react-rails (2.5.0) lib/react/server_rendering/exec_js_renderer.rb:13:in `initialize'
react-rails (2.5.0) lib/react/server_rendering/bundle_renderer.rb:30:in `initialize'
react-rails (2.5.0) lib/react/server_rendering.rb:17:in `new'
react-rails (2.5.0) lib/react/server_rendering.rb:17:in `block in reset_pool'
connection_pool (2.2.2) lib/connection_pool/timed_stack.rb:171:in `try_create'
connection_pool (2.2.2) lib/connection_pool/timed_stack.rb:83:in `block (2 levels) in pop'
connection_pool (2.2.2) lib/connection_pool/timed_stack.rb:79:in `loop'
connection_pool (2.2.2) lib/connection_pool/timed_stack.rb:79:in `block in pop'
connection_pool (2.2.2) lib/connection_pool/timed_stack.rb:78:in `synchronize'
connection_pool (2.2.2) lib/connection_pool/timed_stack.rb:78:in `pop'
connection_pool (2.2.2) lib/connection_pool.rb:93:in `checkout'
connection_pool (2.2.2) lib/connection_pool.rb:62:in `block in with'
connection_pool (2.2.2) lib/connection_pool.rb:61:in `handle_interrupt'
connection_pool (2.2.2) lib/connection_pool.rb:61:in `with'
react-rails (2.5.0) lib/react/server_rendering.rb:26:in `render'
react-rails (2.5.0) lib/react/rails/component_mount.rb:67:in `prerender_component'
react-rails (2.5.0) lib/react/rails/component_mount.rb:34:in `block in react_component'
actionview (5.2.3) lib/action_view/helpers/capture_helper.rb:41:in `block in capture'
actionview (5.2.3) lib/action_view/helpers/capture_helper.rb:205:in `with_output_buffer'
haml (5.0.4) lib/haml/helpers/action_view_xss_mods.rb:6:in `with_output_buffer_with_haml_xss'
actionview (5.2.3) lib/action_view/helpers/capture_helper.rb:41:in `capture'
haml (5.0.4) lib/haml/helpers/action_view_mods.rb:47:in `capture_with_haml'
actionview (5.2.3) lib/action_view/helpers/tag_helper.rb:272:in `content_tag'
haml (5.0.4) lib/haml/helpers/action_view_mods.rb:56:in `content_tag_with_haml'
react-rails (2.5.0) lib/react/rails/component_mount.rb:50:in `react_component'
react-rails (2.5.0) lib/react/rails/view_helper.rb:21:in `react_component'
actionview (5.2.3) lib/action_view/template.rb:159:in `block in render'
activesupport (5.2.3) lib/active_support/notifications.rb:170:in `instrument'
reproduction steps needed

Most helpful comment

Having this issue too and will monitor, thanks.

All 16 comments

lib/execjs/ruby_racer_runtime

Are you using the ruby racer? That ships with LibV8 version 3.
Use a modern JS engine such as miniracer which ships with LibV8 version 7. Yes the ruby racer is 4 major versions behind.

I strongly suspect either your code didn't work with webpacker 3 or upgrading to webpacker 4 itself uses modern JS features.

Thank you for your feedback, I think I'll make a point of putting in the readme about therubyracer as this comes up quite often.

hey @BookOfGreg ,

I've tried to implement your suggestion. I removed therubyracer in favor of:

    libv8 (7.3.492.27.1)
    ---
    mini_racer (0.2.6)
      libv8 (>= 6.9.411)

Now the framework error trace is:

mini_racer (0.2.6) lib/mini_racer.rb:201:in `eval_unsafe'
mini_racer (0.2.6) lib/mini_racer.rb:201:in `block (2 levels) in eval'
mini_racer (0.2.6) lib/mini_racer.rb:286:in `timeout'
mini_racer (0.2.6) lib/mini_racer.rb:200:in `block in eval'
mini_racer (0.2.6) lib/mini_racer.rb:198:in `synchronize'
mini_racer (0.2.6) lib/mini_racer.rb:198:in `eval'
execjs (2.7.0) lib/execjs/mini_racer_runtime.rb:10:in `block in initialize'
execjs (2.7.0) lib/execjs/mini_racer_runtime.rb:66:in `translate'
execjs (2.7.0) lib/execjs/mini_racer_runtime.rb:9:in `initialize'
execjs (2.7.0) lib/execjs/runtime.rb:57:in `new'
execjs (2.7.0) lib/execjs/runtime.rb:57:in `compile'
execjs (2.7.0) lib/execjs/module.rb:27:in `compile'
react-rails (2.5.0) lib/react/server_rendering/exec_js_renderer.rb:13:in `initialize'
react-rails (2.5.0) lib/react/server_rendering/bundle_renderer.rb:30:in `initialize'
react-rails (2.5.0) lib/react/server_rendering.rb:17:in `new'
react-rails (2.5.0) lib/react/server_rendering.rb:17:in `block in reset_pool'
connection_pool (2.2.2) lib/connection_pool/timed_stack.rb:171:in `try_create'
connection_pool (2.2.2) lib/connection_pool/timed_stack.rb:83:in `block (2 levels) in pop'
connection_pool (2.2.2) lib/connection_pool/timed_stack.rb:79:in `loop'
connection_pool (2.2.2) lib/connection_pool/timed_stack.rb:79:in `block in pop'
connection_pool (2.2.2) lib/connection_pool/timed_stack.rb:78:in `synchronize'
connection_pool (2.2.2) lib/connection_pool/timed_stack.rb:78:in `pop'
connection_pool (2.2.2) lib/connection_pool.rb:93:in `checkout'
connection_pool (2.2.2) lib/connection_pool.rb:62:in `block in with'
connection_pool (2.2.2) lib/connection_pool.rb:61:in `handle_interrupt'
connection_pool (2.2.2) lib/connection_pool.rb:61:in `with'
react-rails (2.5.0) lib/react/server_rendering.rb:26:in `render'
react-rails (2.5.0) lib/react/rails/component_mount.rb:67:in `prerender_component'
react-rails (2.5.0) lib/react/rails/component_mount.rb:34:in `block in react_component'

I tried then to save into a file the str evalauted which was raising the error.

The part which is raising the error is the following:

(window["webpackJsonp"] = window["webpackJsonp"] || []).push([["server_rendering"],{

/***/ "./app/javascript/packs/server_rendering.js":
/*!**************************************************!*\
  !*** ./app/javascript/packs/server_rendering.js ***!
  \**************************************************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var babel_polyfill__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babel-polyfill */ "./node_modules/babel-polyfill/lib/index.js");
/* harmony import */ var babel_polyfill__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babel_polyfill__WEBPACK_IMPORTED_MODULE_0__);
// By default, this pack is loaded for server-side rendering.
// It must expose react_ujs as `ReactRailsUJS` and prepare a require context.
var componentRequireContext = __webpack_require__("./app/javascript/components sync recursive ^\\.\\/.*$");

var ReactRailsUJS = __webpack_require__(/*! react_ujs */ "./node_modules/react_ujs/react_ujs/index.js");

ReactRailsUJS.useContext(componentRequireContext);


*/"./app/javascript/packs/server_rendering.js");

/**SOME MORE CODE**/
/***/ })

},[[7,"runtime~server_rendering",0,1,2,3,5,7,6,4,8]]]);
//# sourceMappingURL=server_rendering-b099e93fb426431e6ec8.chunk.js.map

Which is preceeded by:

        var global = global || this;
        var self = self || this;
var console = { history: [] };
['error', 'log', 'info', 'warn'].forEach(function (fn) {
  console[fn] = function () {
    console.history.push({level: fn, arguments: Array.prototype.slice.call(arguments)});
  };
});
function getStackTrace() {
  var stack;
  try {
    throw new Error('');
  }
  catch (error) {
    stack = error.stack || '';
  }
  stack = stack.split('\\n').map(function (line) {
    return line.trim();
  });
  return stack.splice(stack[0] == 'Error' ? 2 : 1);
};

function printError(functionName){
  console.error(functionName + ' is not defined for execJS. See https://github.com/sstephenson/execjs#faq. Note babel-polyfill may call this.');
  console.error(getStackTrace().join('\\n'));
};

function setTimeout() {
  printError('setTimeout');
};

function clearTimeout() {
  printError('clearTimeout');
};

So my guess is that I should define somewhere something like:

var window = window || this;

as it was done for global and self (or maybe something else like console).

This may be related to #615 but it is strange that I see it coming after the upgrade to webpack 4

https://github.com/webpack/webpack/issues/368

Are you using code splitting? I wonder if Webpack/Webpacker4 does something different.
Could be a bug upstream, especially given you've upgraded to a modern JS engine.
Going to re-open for now, but I do think I may not be able to help much as it's likely an issue with Webpack themselves.

Do you have an example repository you could publicly share that exhibits this behavior?

I'm getting this as well. All I'm trying to do is load a "hello world" react component. This appears to be an issue with webpack hot module reloading. When I set hmr to false in config/webpacker.yml, this error goes away (although, I get a diffferent error).

Here is the truncated stacktrace. The top two lines are examined further below.

(execjs):39:41
(execjs):828:10
/Users/ajsharp/.rvm/gems/ruby-2.5.5/gems/mini_racer-0.2.6/lib/mini_racer.rb:201:in `eval_unsafe'
/Users/ajsharp/.rvm/gems/ruby-2.5.5/gems/mini_racer-0.2.6/lib/mini_racer.rb:201:in `block (2 levels) in eval'
/Users/ajsharp/.rvm/gems/ruby-2.5.5/gems/mini_racer-0.2.6/lib/mini_racer.rb:286:in `timeout'
/Users/ajsharp/.rvm/gems/ruby-2.5.5/gems/mini_racer-0.2.6/lib/mini_racer.rb:200:in `block in eval'
/Users/ajsharp/.rvm/gems/ruby-2.5.5/gems/mini_racer-0.2.6/lib/mini_racer.rb:198:in `synchronize'
/Users/ajsharp/.rvm/gems/ruby-2.5.5/gems/mini_racer-0.2.6/lib/mini_racer.rb:198:in `eval'
/Users/ajsharp/.rvm/gems/ruby-2.5.5/gems/execjs-2.7.0/lib/execjs/mini_racer_runtime.rb:10:in `block in initialize'
/Users/ajsharp/.rvm/gems/ruby-2.5.5/gems/execjs-2.7.0/lib/execjs/mini_racer_runtime.rb:66:in `translate'
/Users/ajsharp/.rvm/gems/ruby-2.5.5/gems/execjs-2.7.0/lib/execjs/mini_racer_runtime.rb:9:in `initialize'
/Users/ajsharp/.rvm/gems/ruby-2.5.5/gems/execjs-2.7.0/lib/execjs/runtime.rb:57:in `new'
/Users/ajsharp/.rvm/gems/ruby-2.5.5/gems/execjs-2.7.0/lib/execjs/runtime.rb:57:in `compile'
/Users/ajsharp/.rvm/gems/ruby-2.5.5/gems/execjs-2.7.0/lib/execjs/module.rb:27:in `compile'
/Users/ajsharp/.rvm/gems/ruby-2.5.5/gems/react-rails-2.4.0/lib/react/server_rendering/exec_js_renderer.rb:13:in `initialize'
/Users/ajsharp/.rvm/gems/ruby-2.5.5/gems/react-rails-2.4.0/lib/react/server_rendering/bundle_renderer.rb:30:in `initialize'
/Users/ajsharp/.rvm/gems/ruby-2.5.5/gems/react-rails-2.4.0/lib/react/server_rendering.rb:17:in `new'
/Users/ajsharp/.rvm/gems/ruby-2.5.5/gems/react-rails-2.4.0/lib/react/server_rendering.rb:17:in `block in reset_pool'
[TRUNCATED]

I saved the contents of the source js to a file, on line 39, col 41 is this:

var parentHotUpdateCallback = window["webpackHotUpdate"];

It looks like it's the first line of code that's executed on line 39 is webpack's hot module loading bootstrap code, which is invoked on line 828 of that file, where my app source code begins. Here's a gist of the first 828 lines of that file: https://gist.github.com/ajsharp/ba47b55c1975d6ea6adcf6a863e00113#file-webpack-js-L828. I didn't include the rest b/c it's ~48k lines and is all library code.

So, the hmr stuff expects window to exist, which makes sense. I'm not sure how this is normally dealt with in this library, but that appears to be the offending issue.

hey @BookOfGreg the pr were I am trying this update is this personal project: https://github.com/mberlanda/cheidelacoriera/pull/57

Thank you @ajsharp for providing some clear reproduction steps.

My config of the development server is the following:

development:
  <<: *default
  compile: true

  # Reference: https://webpack.js.org/configuration/dev-server/
  dev_server:
    https: false
    host: localhost
    port: 3035
    public: localhost:3035
    hmr: false
    # Inline should be set to true if using HMR
    inline: true
    overlay: true
    compress: true
    disable_host_check: true
    use_local_ip: false
    quiet: false
    headers:
      'Access-Control-Allow-Origin': '*'
    watch_options:
      ignored: '**/node_modules/**'

Thank you very much for your time and for your help!

Has anyone found a solution for this?

For now I just went back to webpack 3.. :sob:

Opened issue upstream for this. Looks like a Webpack bug:
https://github.com/rails/webpacker/issues/2165

Having this issue too and will monitor, thanks.

I had this issue setting HMR in webpacker.yml to FALSE solved

Here is my _super ugly, very fragile, temporary_ workaround... in case it helps someone until a better solution surfaces. It allows HMR to continue to work on client side and fixes the issue on server side with a hack

In a Rails initializer stick:

# Work around for https://github.com/rails/webpacker/issues/2165
class ReactSSRRenderer < React::ServerRendering::BundleRenderer
  def initialize(options = {})
    @replay_console = options.fetch(:replay_console, true)
    filenames = options.fetch(:files, ['server_rendering.js'])
    js_code = CONSOLE_POLYFILL.dup
    js_code << TIMEOUT_POLYFILL.dup
    js_code << options.fetch(:code, '')

    filenames.each do |filename|
      js_code << asset_container.find_asset(filename)
    end

    # Monkey patch the HMR to immediately fallback to just using `require`
    js_code = js_code.gsub('var me = installedModules[moduleId];', 'return __webpack_require__;')
    # Monkey patch the HMR to have an in-scope 'window' object.
    js_code = js_code.gsub(
      'var parentHotUpdateCallback = window["webpackHotUpdate"];',
      'var window = {}; var parentHotUpdateCallback = window["webpackHotUpdate"];'
    )
    @context = ExecJS.compile(GLOBAL_WRAPPER + js_code)
  end
end

Rails.application.config.react.server_renderer = ReactSSRRenderer

If anyone knows a nicer solution eg by changing webpack config (see for example https://github.com/shakacode/react_on_rails_tutorial_with_ssr_and_hmr_fast_refresh/commit/8e3bad711c318ceadff9edeb4895592aa845812d) please let us all know :)

@stevegeek, I'd love to brainstorm with you to better understand what the issue is. My email is on my Github profile.

Or can you explain the lines in your "fragile hack"?

@justin808 the issue is basically https://github.com/rails/webpacker/issues/2165 which I know you looked at before. ie the bundle contains HMR logic which references window.

Im not sure now to apply the same idea from https://github.com/shakacode/react_on_rails_tutorial_with_ssr_and_hmr_fast_refresh/commit/8e3bad711c318ceadff9edeb4895592aa845812d, ie create a separate pack for SSR without HMR.

The hack here avoids finding a good solution by simply stepping in at the last second and modifying the output of the bundle when doing SSR.

The first line essentially disables HMR logic by not attempting to load modules using it but instead just uses require (ie we just return __webpack_require__ instead) , and secondly creates an object called window before the code that needs it to prevent the exception! Very hacky!

ie the bundle contains HMR logic which references window.

That's because of the webpacker configuration. The SSR configuration needs to be different. Does react-rails support having different configurations for client and server-rendering? The SSR part naturally should not have any references to HMR.

Yeah that’s why I was thinking it would be good to do something like what
you did in the commit I referenced above but I don’t know how in
react-rails!

On Sun, 6 Dec 2020 at 23:39, Justin Gordon notifications@github.com wrote:

ie the bundle contains HMR logic which references window.

That's because of the webpacker configuration. The SSR configuration needs
to be different. Does react-rails support having different configurations
for client and server-rendering? The SSR part naturally should not have any
references to HMR.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/reactjs/react-rails/issues/985#issuecomment-739577118,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAALHPXQUPYHAQIS6PAP73TSTQB2JANCNFSM4HMJY4DA
.

I'm not sure if this is relevant, but I had this issue and what solved it was to set the output.globalObject in the webpack config to (typeof self !== 'undefined' ? self : this).

I found this solution in another issue, here: https://github.com/reactjs/react-rails/issues/970#issuecomment-604864425

Was this page helpful?
0 / 5 - 0 ratings

Related issues

adoseofjess picture adoseofjess  Â·  4Comments

ttanimichi picture ttanimichi  Â·  4Comments

axhamre picture axhamre  Â·  3Comments

mmccall10 picture mmccall10  Â·  4Comments

davidlormor picture davidlormor  Â·  3Comments