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

Is there any way of using fetch on the server for isomorphic rendering?

Open DamianPereira opened this issue 6 years ago • 9 comments

Steps to reproduce

Require isomorphic-fetch or node-fetch on server_rendering.js

Expected behavior

Fetch should work, and it should finish the request (and callback) before generating the html for the browser.

Actual behavior

I'm requiring node-fetch with const fetch = require('node-fetch');. Using it results in

<ExecJS::ProgramError: ReferenceError: fetch is not defined>.

If, instead, I do const fetch = require('isomorphic-fetch'); I get

Unhandled promise rejection (rejection id: 2): ReferenceError: XMLHttpRequest is not defined.

System configuration

Sprockets or Webpacker version: 3.0.2 React-Rails version: 2.4.2 Rect_UJS version: 2.4.2 Rails version: 5.1.4 Ruby version: 2.4.1


How can I solve this? I want a product list in an app to be filled with data from an API, this currently works on client-rendering, doing a fetch call in componentWillMount and calling this.setState with the response (while showing a loading screen meanwhile).

But for server-side rendering I need the data to be inserted when the server compiles it. I don't know how to make it work. Maybe I could:

  1. Make my products component stateless, with the product list as props.
  2. Implement a custom renderer which does the api call and load the response as props in an App component.
  3. Make a wrapper component for the product list which only tries to fetch the data if the props from the app are empty.

The problem is I'd still have to do the requests in Ruby, and it seems awfully complex for something I could solve just using fetch on the server. Would the solution above work? Is there any easier solution?

DamianPereira avatar Nov 29 '17 23:11 DamianPereira

Hi @DamianPereira , Do you have a small example of using that library that I can run?

Requesting things from JS server-side should theoretically just work and I'll see if I can give you a hand if you have a small example.

In our production apps, we put the initial state in using props as we find working in ruby simpler.

BookOfGreg avatar Dec 03 '17 11:12 BookOfGreg

Have a look at http://ruby-hyperloop.org (the hyper-react gem). Look at isomorphic helpers module for how to get this to work...

catmando avatar Dec 03 '17 16:12 catmando

@BookOfGreg Hi! What library do you mean? Fetch?

Requesting things from JS server-side should theoretically just work

I haven't been able to use any form of fetch on the server. It results in the errors above, it seems ExecJS can't use node environment, so that might be the reason isomorphic-fetch does not work.

@catmando I will check it out, thanks!

DamianPereira avatar Dec 04 '17 14:12 DamianPereira

catmando This one? https://github.com/ruby-hyperloop/hyper-react/blob/master/lib/reactive-ruby/isomorphic_helpers.rb

I haven't seen anything applicable there. JS server-side fetch is really necessary - to optimize the Rails data loading on SSR. Currently I have to pre-load everything via props - whereas I would prefer to load it dynamically in portions.

programrails avatar Aug 09 '18 15:08 programrails

Is there any update on this issue?

Does react-rails really not support fetch on the server? It's kind of misleading to claim that react-rails supports isomorphic javascript and not mention that it doesn't support fetch on the server.

tylerkahn avatar Jan 12 '19 21:01 tylerkahn

@tylerkahn Provide an example and I'll take a look if it should work or not. If you complain without trying to contribute to the community, people will be less likely to try help.

Edit: The Reproduction steps needed label should have been a hint. Help me to help you.

BookOfGreg avatar Jan 14 '19 09:01 BookOfGreg

@BookOfGreg First of all I want to be clear that I think this is a really great project.

I spent about 8 hours in total trying to diagnose this particular problem and get it to work (to no avail unfortunately) so I do have some skin in the game here.

I guess my question is, is isomorphic fetch known to work with react-rails?

As far as I can tell, simply importing “isomorphic-fetch” anywhere in the app causes prerendering to break which is what a lot of dependencies that claim to be isomorphic use.

I tried to split up the client and server webpacker configs but I couldn’t get it to actually build a server build artifact that didn’t pull in the browser version. This breaks prerendering in the precise manner as described in the bug.

The repro steps would be:

  1. do a fresh install of react-rails

  2. npm install isomorphic-fetch

  3. add require(“isomorphic-fetch”) to your application.js pack

  4. set prerender to true in react_component

I’m not by a computer at the moment but I can provide additional details if you need.

Thank you

tylerkahn avatar Jan 14 '19 09:01 tylerkahn

@tylerkahn Sorry for my short response to you previously, it can be difficult being a maintainer at times though that doesn't excuse me being rude.

I've personally never used isomorphic-fetch or fetch directly and never seen anyone confirm it as working. The main way of using this gem in an isomorphic way is SSR and passing props, though this is clearly not what you're currently after. I assume you're also not trying to statically serve the JS and fetch the data back via props. Any deeper serverside integration is not really possible as ExecJS can't call back into the ruby env to my knowledge so everything has to be passed in serverside at the prop boundary. If you could provide an example repo then I will try look into if something can be done.

We're also always looking for additional contributors and maintainers so if you feel you could help with this then would very gladly accept the help.

BookOfGreg avatar Jan 14 '19 10:01 BookOfGreg

@tylerkahn Your issue is you're adding isomorphic-fetch to application.js, not server_rendering.js.

server_rendering.js is the entry point for server rendering, so any polyfills you need, need to imported there.

I created an example for you: https://github.com/HorizonShadow/ssr-fetch/blob/master/app/javascript/packs/server_rendering.js

Note this isn't going to fetch the data before rendering. This isn't possible in react because all fetches are asynchronous. When react server runs renderToString, it takes the output of the first render call, and returns it as a string. Since fetch is asynchronous, the first render is never going to contain your data.

That said, the react team is working on bringing that functionality to react server in 2019: https://reactjs.org/blog/2018/11/27/react-16-roadmap.html#suspense-for-server-rendering

joshleblanc avatar Jan 30 '19 18:01 joshleblanc

Based on the discussion above, closing this issue. We have had no updates on this since 2019.

alkesh26 avatar Nov 01 '22 08:11 alkesh26