Skip to content

Making the helper for server side rendering work with JS created by Webpack #301

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

Closed
justin808 opened this issue Jun 19, 2015 · 27 comments
Closed

Comments

@justin808
Copy link
Collaborator

I want to enable the system of externally packaged js code (via Webpack) work with the server rendering library here: https://github.com/justin808/react-webpack-rails-tutorial

While I can and probably should make a separate project, I was wondering if it might make sense to submit a PR to this project. In a nutshell, the webpack system creates one JS file that the server side rendering would use.

So there's just a tiny part of this project that's relevant which is mostly the view helpers.

@chrisvfritz
Copy link

👍 I also prefer using Webpack to manage my JS, so tighter integration would be helpful for me as well.

@rmosolgo
Copy link
Member

Yeah, I think think this would be good fit! We have a problem with Sprockets in production, too: although sprockets might create a big, compiled file, it's not used by react-rails server rendering (#212 & #288).

How about this solution:

  • You could implement CompiledFileRenderer (your call on the name :P) which builds on ExecJSRenderer
  • CompiledFileRenderer accepts a path to file which should be a compiled asset including everything needed for server rendering: React.js, components and any dependencies

For Webpack users, they could use this renderer in dev & production, just pointing it at their bundle. For Sprockets users, they could use this in production and point it at the compiled asset.

One more consideration: Since Sprockets files often have a big hash in them which we can't predict, the path argument for this renderer should be suitable for Dir.glob (or something similar). That way Sprockets users won't have to worry about different hashes on different builds.

What do you think about that? It would be a big win for react-rails I think!

@chrisvfritz
Copy link

One minor addition I'd make is the ability to pass an array of file paths. I personally prefer to bundle my vendor assets separately from my application assets, resulting in two files. The reason is my vendor assets rarely change, but are (typically) the largest contributor to file size, while my (again, typically) smaller application code might update on production several times per day. For mobile users, having to redownload a much smaller file can make a noticeable performance difference.

@justin808
Copy link
Collaborator Author

We should probably have a skype to do some dual code reading. I don't know enough about Sprockets. However, I read the view helper code carefully. The thing about my webpack strategy is that all the configuration for the JS code is in the webpack config file, and ALL the js assets are underneath a /client directory so you can open up the JS code in a JS editor, like WebStorm. Based on reading some react-rails code, it seems like the trick is to make whatever react components global for running execjs. When webpack gets done with it's thing, it just creates one honking es-5 JS file that's got everything neatly bundled. That file is excluded from source code control.

Let's talk more on skype or slack.

@mchristen
Copy link

I would be careful using a glob to find the pre-compiled assets. sprockets-rails makes no guarantee that there won't be more than one version of the asset file. It specifically keeps around older copies on a rolling basis that is configurable.

See the documentation here for the specifics.

My workaround for that was to use a new ActionView::Base instance to call asset_path on, like so

ActionView::Base.new.asset_path('components.js')

@justin808
Copy link
Collaborator Author

The key question is whether it makes sense to have a separate gem that just has the helper methods, and then this gem could depend on the gem with the helpers.

I think this gem includes the react source and has close integration with sprockets.

My solution just requires including one specified JS file that is prepared by webpack either by a watch process during development (using foreman to start, for example), or pre-compiled.

The technique is explained here:
http://www.railsonmaui.com/blog/2014/10/03/integrating-webpack-and-the-es6-transpiler-into-an-existing-rails-project/

@rmosolgo
Copy link
Member

@mchristen I guess you're right about possibly multiple compiled assets. I wonder if you could use the asset_path approach in the config, eg

if Rails.env.production?
  react.config.server_renderer = React::Rails::ServerRendering::StaticFilesRenderer
  react.config.server_renderer_options = {
    files: [ActionView::Base.new.asset_path("components.js")]
  }
else 
  # SprocketsRenderer etc
end

That would keep the renderer very "dumb" -- still just takes a list of files

@nikhilbaradwaj
Copy link
Contributor

How about the following - When you run the installation script for react-rails gem, it creates a file called components.js and a directory called 'components' under 'javascripts'.

  1. remove the components folder
  2. components.js will require everything under the components folder. Remove this reference and just add a reference to the webpack pre-compiled js file (rails-bundle.js in the case of justin808's application).

Adding <%= react_component('ReactComponentName', {name: 'Something'}, {prerender: true or false}) %>
to the erb/html file should then work for both server side and client side rendering assuming that 'ReactComponentName' react component is globally accessible in the compiled js.

@justin808
Copy link
Collaborator Author

I'll create a fork and throw up a PR for us to play with. It will be really fun to get this working. I'll post a message once this is somewhat working. If anybody wants to work with me on this, please email me: [email protected].

@justin808
Copy link
Collaborator Author

I published an alpha version here: https://rubygems.org/gems/react_on_rails.

It relies heavily on webpack. I'd be interested in possibly merging this code, but I'm not sure it makes sense since the approach is a bit different.

The API is a little bit different to support redux. You create a function that takes props and returns a React component rather than just rendering a react component.

Here's the source:
https://github.com/shakacode/react_on_rails/

@taylorbrooks
Copy link

Like @nikhilbaradwaj, I really only want to use react-rails for the view helpers and webpack for compilation and dependencies.

It would be awesome to have a bundled file (generated by webpack) like components.js -- which would include all the things.

And whenever I used the view helper <%= react_component("ComponentThing" ..... %> it would be smart enough to find that component in my bundled js file.

@justin808
Copy link
Collaborator Author

@taylorbrooks What are we missing in the gem https://github.com/shakacode/react_on_rails/ (BTW -- just about to merge a very important PR).

@justin808
Copy link
Collaborator Author

@rmosolgo We've got react-rails mentioned a couple of times in our https://github.com/shakacode/react_on_rails/blob/master/README.md for react_on_rails.

Would you be amenable to a link to react_on_rails from your README as an alternative if one wants to use webpack? BTW, @chrisvfritz has tried out react_on_rails 😄.

@rmosolgo
Copy link
Member

Yes, I think a "Related Projects" section would be a nice addition! There are a few other projects (eg React.rb, react-rails-benchmark_renderer) that could have a line there, too!

@mxmzb
Copy link

mxmzb commented Jun 26, 2016

Why do we need to create something new everytime. Is there a reason I don't see why react-rails and react_on_rails could not have been initially be done as one thing (or one of them extended by the features of the other) with a bigger community rather than having two smaller and less used gems?

@justin808
Copy link
Collaborator Author

@thelamborghinistory Both react-rails and react_on_rails are two completely legit and different ways of attacking the same problem.

  • react-rails is focused on the pure "Rails" way of using the asset pipeline at a deep level.
  • react_on_rails is focused on a webpack based technique that keeps the front end code very much in the JavaScript land of tooling. This enables advanced JavaScript features such as CSS Modules and hot reloading. This technique would also make disassociation from Rails relatively easy.

@thelamborghinistory Does this answer your question?
@rmosolgo Did I miss anything?

@mxmzb
Copy link

mxmzb commented Jun 26, 2016

@justin808 not at all, the question wasn't meant to be on that concrete level. But if you already answer like that, can you even expand, what will differentiate the two gems after react-rails 2.0, which is going to support webpack way, too (see #448)?

See, my issue was more of a general nature. I am just getting pissed off more and more nowadays with developers, as in general everyone behaves like he needs to do some own thing instead of extending existing solutions to his needs.
All that variety in software is great, cause that what "evolves" and "revolutionize" things, but in practice I'd rather prefer more opinionated solutions that would benefit from a bigger community and stability. The fact that my anger about that emerged in this very Github issue is pure coincidence.

@chrisvfritz
Copy link

@thelamborghinistory As a maintainer of other large projects, I just wanted to let you know that these issues are definitely not the right place for your anger about "developers nowadays". You can save that for a blog post.

@etcook
Copy link

etcook commented Jun 26, 2016

@thelamborghinistory Pissed off? You're more than welcome to start or fork your own project and manage it as you see fit. Even if the projects overlapped a great deal, it's not unusual for projects to fork and some of those features eventually being integrated upstream. Forking and fragmentation is a core part of the FOSS ethos - one of the mechanisms by which the community balances/reconciles project stability and focus with developmental progress.

react_on_rails is a very different beast. It builds on top of react-rails and adds many features which are outside the scope of the react-rails charter. Although some of the concepts employed in react_on_rails might eventually be integrated into react-rails, the distinction between the two is not minor. React_on_rails leans towards isomorphic React apps that utilize the Rails backend as a separate api layer, not just integrating Rails + helpers into the Rails ecosystem.

@justin808
Copy link
Collaborator Author

See, my issue was more of a general nature. I am just getting pissed off more and more nowadays with developers, as in general everyone behaves like he needs to do some own thing instead of extending existing solutions to his needs.

Personally, I have an attitude of gratitude with open source. IMHO, it's one the greatest things that's ever happened to our industry.

I have written a very detailed response here on medium.com:

An Attitude of Gratitude for Open Source

If you read this and agree, please do click the heart at the bottom of the medium page, as this encourages medium to share this article more widely.

I'm not going to repeat most of what I wrote there. However, for this discussion, some very specific differences between react-rails and react_on_rails v2 are:

Here’s a few of the key differences I see between React on Rails now and the v2 of react-rails:

  • react_on_rails is available now for Webpack with many live sites listed and probably many more unlisted, at our PROJECTS.md page.
  • Default configuration: Since Webpack will be an add-on for react-rails, it might not have the unified community around it compared to the React on Rails community, backed by my team from ShakaCode. Our community is based around native JavaScript tooling, including webpack and npm.
  • Backwards compatibility: Since react-rails will strive to make their solution an easy upgrade, Webpack will probably not be the default option, as the feature proposal for v2 of react-rails is: Improve the flexibility of React addons: Support custom ReactJS builds (backed with npm & webpack, built into the apps asset directory).

To be honest, I’m not the one to speak on v2 react-rails, as I’m only aware of the public feature list posted on github.

Incidentally, react_on_rails uses Semantic Versioning, so we bump our major version number for any deprecation or other minor configuration changes. Even though we're on v6.x, the changes to upgrade have been very straightforward, as listed in our CHANGELOG.md.

@mxmzb
Copy link

mxmzb commented Jun 26, 2016

@chrisvfritz I seem to have formulated wrongly. I am not pissed of by "the developers" (in a way, I like to call myself developer, too, and please believe me that it's not my intent to offend anyone with that, nor you nor myself nor anyone at all, I just tend to formulate things wrongly more often).
I am pissed of by the fact, that there aren't just more great different technologies than you could ever try out in your life, but there are even more different approaches again to implement these technologies (e.g. we got rails, we got react, and instead of one opinionated gem we got - as usual - two, or even more, (don't know, as I didn't look further)). As I said in my previous comment, the outburst of my personal feelings in this very thread is pure coincidence and I might indeed take this to a blog post.

However, I don't think the main statement I am trying to make is wrong here. The issue title says "Making the helper for server side rendering work with JS created by Webpack" and I am basically asking why it wasn't or isn't possible to extend / merge react-rails instead for the sake of better / bigger community and therefore provide more and better help resources ( @justin808 even suggested to do so earlier in this thread, but backed down, cause he was thinking the two gems vary too much. I think differently regarding this point as you might have noticed).
Everything seems to hint that react-rails is going to take the same direction as react_on_rails and the outlook of the differences in fact seems minor to me. So: Why could this not become one bigger thing instead of two smaller in the future?

@etcook
Copy link

etcook commented Jun 26, 2016

@thelamborghinistory Have you given react_on_rails more than a cursory glance? Respectfully, it sound like you're arguing from a position of what you think react_on_rails might be.

Your FOSS "utopia" taken to its end, would consist primarily of monolithic projects, with very little focus or discipline, paralyzed by legacy requirements. Concurrent development of concepts, especially when they're as distinct in intention as these two projects, is a good thing. This is also how the community explores concepts, tests best practices, etc.

I [need to] take this to a blog post.

@mxmzb
Copy link

mxmzb commented Jun 26, 2016

@thelamborghinistory Have you given react_on_rails more than a cursory glance? Respectfully, it sound like you're arguing from a position of what you think react_on_rails might be.

@etcook believe it or not, but I am using it, not react-rails.

@catmando
Copy link

catmando commented Jul 5, 2016

Did this issue ever get resolved? I.e. is there any way to add webpack assets to react-rails and have pre-rerendering work?

@justin808
Copy link
Collaborator Author

@coryasato
Copy link

@catmando Been playing with this all day. Got it working but I'm still unsure if its clean or not. Well, exposing components globally is pretty uggs but necessary here.

I ended up creating a separate "server_bundle.js" specific for this gem. That file gets picked up by a another server.js file with the react-server helper.

// server.js
//= require react-server
//= require server_bundle
# config/environments/development
 config.react.server_renderer_options = {
    files: ["server.js"], 
  }

I expose React and ReactDOM in my webpack config via the webpack-expose-loader. I use the same loader to expose all our components to window.

// Example file that'll get picked up by webpack.
// expose window.SampleApp from directory/file thats exporting the module.
require('expose?SampleApp!./sampleApp.js');
// Snippet of our webpack.base.config file.
module: {
    loaders: [
      {
        test: require.resolve('react'),
        loader: 'expose?React'
      },
      {
        test: require.resolve('react-dom'),
        loader: 'expose?ReactDOM'
      }
    ],
    ...
  },

You'll also need to import and expose the component in your client bundle as well. We have that being //= required in application.js as per norm.

The one thing I'm trying to figure out, and why I was crawling these issues, is how to get Rails to see the changes made to the server.js bundle without updating the view's controller or reloading the app.

@rmosolgo
Copy link
Member

rmosolgo commented Dec 9, 2016

Thanks for sharing some solutions here! I'll keep an eye out for forthcoming Rails 5.1 asset changes too, and see if there's anything react-rails can take advantage of.

@rmosolgo rmosolgo closed this as completed Dec 9, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants