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

HMR and Server-Side rendering results in window is not defined #925

Closed
arminfro opened this issue Aug 21, 2018 · 15 comments
Closed

HMR and Server-Side rendering results in window is not defined #925

arminfro opened this issue Aug 21, 2018 · 15 comments

Comments

@arminfro
Copy link

arminfro commented Aug 21, 2018

Steps to reproduce

  • start webpack-dev-server
  • <%= react_component('HelloWorld', { greeting: 'hello' }, prerender: true) %>

webpacker.yml ( diff to default, docker environment )

dev_server:
    host: 0.0.0.0
    public: 0.0.0.0:3035
    hmr: true

Expected behavior

Rendering of page

Actual behavior

ExecJS::ProgramError ReferenceError: window is not defined

System configuration

Sprockets or Webpacker version: 3.12.0
React-Rails version: 2.4.7
Rect_UJS version: 2.4.4
Rails version: 4.2
Ruby version: 2.3


When I request the page without server rendering (e.g. <%= react_component('HelloWorld', { greeting: 'ho' }) %>) or when I disable HMR (e.g. hmr: false) it works fine. But leaving it results in described error.

Since there is a fixed issue with the same error in #615 I wonder if I'm missing something obvious? Something like prerender_polyfill.js doesn't seem to work in my case.

Stack trace beginning from a view file:

/app/javascript/components recursive ^\.\/.*$.map../GoodbyeWorld ((execjs):39:41)
(execjs):762:10
(execjs):34182:14
global ((execjs):1:102)
Object.<anonymous> ((execjs):1:120)
Module._compile (module.js:652:30)
Object.Module._extensions..js (module.js:663:10)
Module.load (module.js:565:32)
tryModuleLoad (module.js:505:12)
Function.Module._load (module.js:497:3)
execjs (2.7.0) lib/execjs/external_runtime.rb:39:in `exec'
execjs (2.7.0) lib/execjs/external_runtime.rb:14: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.4.7) lib/react/server_rendering/exec_js_renderer.rb:13:in `initialize'
react-rails (2.4.7) lib/react/server_rendering/bundle_renderer.rb:30:in `initialize'
react-rails (2.4.7) lib/react/server_rendering.rb:17:in `new'
react-rails (2.4.7) 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.4.7) lib/react/server_rendering.rb:26:in `render'
react-rails (2.4.7) lib/react/rails/component_mount.rb:67:in `prerender_component'
react-rails (2.4.7) lib/react/rails/component_mount.rb:34:in `block in react_component'
actionview (4.2.10) lib/action_view/helpers/capture_helper.rb:38:in `block in capture'
actionview (4.2.10) lib/action_view/helpers/capture_helper.rb:202:in `with_output_buffer'
haml (5.0.1) lib/haml/helpers/action_view_xss_mods.rb:6:in `with_output_buffer_with_haml_xss'
actionview (4.2.10) lib/action_view/helpers/capture_helper.rb:38:in `capture'
haml (5.0.1) lib/haml/helpers/action_view_mods.rb:47:in `capture_with_haml'
actionview (4.2.10) lib/action_view/helpers/tag_helper.rb:106:in `content_tag'
haml (5.0.1) lib/haml/helpers/action_view_mods.rb:56:in `content_tag_with_haml'
react-rails (2.4.7) lib/react/rails/component_mount.rb:50:in `react_component'
react-rails (2.4.7) lib/react/rails/view_helper.rb:21:in `react_component'
app/helpers/application_helper.rb:389:in `react_component'
app/views/shop_manage/galleries.html.erb:12:in `_app_views_shop_manage_galleries_html_erb__1251111634569882468_205190840'

In case you wonder react_component in application_helper does nothing exciting, just sets instance variable and calls super.

@BookOfGreg
Copy link
Member

This is a common error, normally caused by someone trying to include npm packages which only run client-side.

Just tested on a blank rails app with webpack-dev-server running and rendering server-side.
image

It's possible you have included some JS that is badly behaved and tried to access Window when rendering server-side. That package wouldn't run on a normal NodeJS server so it won't run in SSR anyway.

What I checked:

rails new foo --webpack=react
cd foo
# Add react-rails to gemfile
bundle install
rails g scaffold bar
rails g react:install
rails g react:component bar
# add <%= react_component 'Bar', {}, prerender: true %> to index.html.erb
# add <%= javascript_pack_tag 'application' %> to application.html.erb
# run bin/webpack-dev-server in one tab
# run rails s in another tab
open http://localhost:3000/bars

Can you provide a more complete reproduction? The gem works when following the start of the Readme in the standard case, so there is information that I'm missing in order to help you.

@arminfro
Copy link
Author

arminfro commented Aug 23, 2018

Reproduction script:

#!/bin/bash 

gem install rails --version=4.2.0 --no-ri --no-rdoc
rails _4.2.0_ new foo
cd foo
bundle install
bin/rails g scaffold bar
echo "gem 'webpacker'" >> Gemfile
echo "gem 'react-rails'" >> Gemfile
bundle install
bin/rake webpacker:install
bin/rake webpacker:install:react
bin/rake db:migrate RAILS_ENV=development
bin/rails g react:install
bin/rails g react:component HelloWorld greeting:string
echo "<%= react_component('HelloWorld', { greeting: 'hello' }, prerender: true) %>" >> app/views/bars/index.html.erb
# adding "<%= javascript_pack_tag 'application' %>" to `body` in app/views/layouts/application.html.erb
# change 'hmr: false' to 'hmr: true' in 'config/webpacker.yml'
# start 'bin/webpack-dev-server'
# start 'bundle exec rails server -e development'
# request 'http://localhost:3000/bars'

The last five steps needs to be done manually, the lines before can be executed as a shell script and results in the described ReferenceError: window is not defined

@arminfro arminfro changed the title HMR and ServerSide Rendering results in window is not defined HMR and Server-Side rendering results in window is not defined Aug 23, 2018
@BookOfGreg
Copy link
Member

BookOfGreg commented Aug 23, 2018

Thank you a bunch for the reproduction, that clears it up.

I'll make a guess that HMR is causing SSR to fail, which would make sense as there is no reloading in a serverside world.

We don't currently have the ability to support HMR so I'm unable to help you at this time. It is a desirable feature though.

@arminfro
Copy link
Author

Thank you for the confirmation ( and for your work on react-rails gem ;)

My team is just starting to migrate our project to use react-rails and webpacker, so this is all new to us.

As you pointed out it does not make sense to have HMR in server rendering mode, but as far as I understand it, this is only true for the first rendering, isn't it?
Cause all the webpack (and react) sources gets send to the client anyway and further renderings will be done on client-side. That brings me to the assumption that webpacker just needs a case for not relying on the window object on first render, which (maybe) means the issue should be addressed in webpacker gem, I'm not sure.

@BookOfGreg
Copy link
Member

For now I recommend not using HMR when using SSR in this gem. It's part of the longer-term plan that I would like it to work successfully but I haven't had the time to be able to enable it yet.

It's possible to get them to work together as I'm keeping my eye on the renchap/webpacker-react project as they are working at merging the HMR with SSR in this PR.

It's definitely our project to sort as the upstream webpacker gem does let you use HMR, we just need to be clever about not running the module.hot code serverside.

@arminfro
Copy link
Author

Interesting, thanks for the explanation! I'm looking forward to it 😊

@andrewscarani
Copy link

Is there any way for me to configure react-rails such that it would ignore prerender: true when hmr: true in webpacker.yml? Thus I could turn SSR on/off via the single hmr flag?

@gencer
Copy link

gencer commented Dec 17, 2019

Interestingly, I have hmr set to false. But still face with this issue.

@gencer
Copy link

gencer commented Dec 17, 2019

Well, It is because somewhere in the config i setup jQuery. This was the reason for window issue.

@justin808
Copy link
Collaborator

justin808 commented Jun 14, 2020

I'm facing the same issue with the new version of https://github.com/shakacode/react_on_rails. With the simplest hello_world example that uses the same bundle for both client and server rendering from the running of bin/webpack-dev-server with the default rails/webpacker setup, 5.1.1, server rendering errors out due to references to window.

One cause of this reference to window is that the default globalObject value is "window." Setting that value to "this" on the server bundle solves that problem. There might be others like this.

I suspect that it makes much more sense to just skip the idea of having the webpack-dev-server produce bundles for SSR.

My solution for React on Rails is to have a separate bundle for SSR and to use bin/webpack --watch. I'm wondering if that solution would work for this gem? I'm not clear on how you could specify a different server bundle from the client bundles in the setup of react-rails.

Also, for hot reloading, react-hot-loader is deprecated. I'm having good luck in using https://github.com/pmmmwh/react-refresh-webpack-plugin. The setup is dead simple. The only tricky part is to make sure this is not included in the server bundle, and if you're using loadable-components, then you want to have a setup that allows running HMR/hot-reloading without loadable-components.

Just to give full disclosure, I'm the creator of the https://github.com/shakacode/react_on_rails. I'm actively working in this area as my company https://www.shakacode.com/react-on-rails-pro is providing support for the integration of React with Ruby on Rails. That includes support for vanilla rails/webpacker and this gem. For those of you digging into the rails/webpacker part of the webpack config, you might find my RailsConf 2020 talk helpful: Webpacker, It-Just-Works, But How?.

@justin808
Copy link
Collaborator

I've made some more progress on this issue:

If you are using server-side rendering, the bin/webpack-dev-server can work for both the client and server bundles so long as these values are set:

dev_server.hmr maps to devServer.hot. This must be false if you're using the webpack-dev-server for client and server bundles.
dev_server.inline maps to devServer.inline. This must also be false.

@mariaxtina
Copy link

Wanted to follow up and confirm that it's still not recommended to allow for HMR when using SSR in this gem?

I know @BookOfGreg mentioned it was part of a longer term plan to allow, so just wanted to see if it's been revisited. Thanks!

@justin808
Copy link
Collaborator

@mariaxtina we've got HMR working nicely with SSR, Webpack 5, and pretty much everything with https://github.com/shakacode/react_on_rails. There should be no reason this won't work for react-rails. If anybody here is interested support for react-rails, please see https://www.shakacode.com/react-on-rails-pro/. I'd be happy to provide support.

@jakeonfire
Copy link

see #985 (comment) for a react-rails-specific solution.

@alkesh26
Copy link
Collaborator

alkesh26 commented Nov 3, 2022

Closing this issue. Please refer to 985 comment.

@alkesh26 alkesh26 closed this as completed Nov 3, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants