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

Debugging Solutions for Prerender

Open blainekasten opened this issue 9 years ago • 18 comments

In my experience, when using prerender: true, if something breaks the server side rendering, the error messages are less than helpful.. My workflow has become anytime that happens to stop prerendering and use browser tools to diagnose where the issue is. The stack trace provided by the gem is no where near the help that comes from browser tools.

Have we thought of the idea of in the case of a prerender error to not throw an error, but instead disengage the prerendering. Maybe wrap the code that caused the server error in a debugger; call to help users know something is wrong?

maybe even include an alert message. So something like this is what I have in mind:

prerender catches error

  • find error location
  • Inject the following above the error
alert('ReactRails encountered an error server side rendering, please inspect your issue with your browser dev tools');
debugger;
// error code is here.

thoughts?

blainekasten avatar May 14 '15 15:05 blainekasten

In my experience this is a pain point as well. My hackish remedy was to monkeypatch React::Renderer.reset_combined_js! to write out the contents of @@combined_js to a file in tmp/

You can at least this way match up line numbers from the stack trace to actual javascript code, still not ideal but has helped me solve problems with server side rendering.

mchristen avatar May 14 '15 18:05 mchristen

yeah. I could also see something cool like that being printed to the ruby console. a good example of an error indicator is the babel compiler. When it encounters errors, it spits out a syntax highlighted output of the error line and the surrounding lines. That could also be a great solution.

blainekasten avatar May 15 '15 16:05 blainekasten

:+1: Recently I'm using server-rendering more myself, and I've had the same workflow (first make it perfect, then prerender: true). I'd be happy to pitch in or try something out along the way!

rmosolgo avatar May 15 '15 17:05 rmosolgo

@rmosolgo Before you do server pre-rendering, do you get real React errors/debugging? Right now whenever there is a React error in my app, the component doesn't render and fails silently. No errors on rails server or client side in chrome console.

barefootford avatar Aug 19 '15 00:08 barefootford

huh, yeah I usually get normal React errors (or a such-and-such is undefined if I have a typo in the react_component helper). Happy to help debug if you care to share some code. I think I'm on React 0.13, not sure if anything changed since then.

rmosolgo avatar Aug 19 '15 02:08 rmosolgo

I second @barefootford, whatever error in React I only get "SyntaxError: unknown:" and reference to javascript_include_tag 'application':

react_error

It would be so helpful to get normal React errors! :)

axhamre avatar Aug 24 '15 12:08 axhamre

I'm not sure if there is something that I'm configuring differently, but I had the same story with the first project I used with react-rails, but on my second one I'm getting errors server side and in the console depending on the issue. I haven't had time to debug and figure out what the difference is.

barefootford avatar Aug 24 '15 15:08 barefootford

same problem here. In my case, I patch ComponentMount as follows to retry rendering with prerender: false when something goes wrong during server rendering:

# config/initializers/react-rails.rb
class React::Rails::ComponentMount
  def react_component_with_auto_retry(name, props = {}, options = {}, &block)
    react_component_without_auto_retry(name, props, options, &block)
  rescue
    if options[:prerender] && Rails.env.development?
      react_component_without_auto_retry(name, props, options.merge(prerender: false), &block)
    else
      raise
    end
  end

  alias_method_chain :react_component, :auto_retry
end

yuku avatar Oct 15 '15 03:10 yuku

Any other insight as to how to debug with prerender:true. I'm getting an error but there is no indication whatsover where it comes from so I really don't know what to do next. It all works well with no prerender... Thanks!

shaimo avatar Dec 13 '15 07:12 shaimo

@shaimo Same thing here. No JS warnings without prerender, but as soon as I turn it on I get errors "can't parse [...]"

I'll try updating my javascript runtime, but I am not having any luck yet.

tyler-boyd avatar Jan 06 '16 02:01 tyler-boyd

Old thread I know, but putting this here for anyone who might find it in future as I didn't really find much online. Basically a modified version of the above which didn't work for me on Rails 5.1, Ruby 2.5.3. This will catch errors rendering and try again without pre-rendering it. We then have an error boundary in the React code that will catch any errors with componentDidCatch and POST them to our API which logs them. It doesn't help track down errors that are specific to SSR, but at least means users won't get a 500, and for errors that affect client side rendering too, they will get a friendly error page and we will get a report.

# config/initializers/react-rails.rb
class React::Rails::ComponentMount
  old_react_component = instance_method(:react_component)

  define_method(:react_component) do |name, props = {}, options = {}|
    begin
      old_react_component.bind(self).(name, props, options)
    rescue
      old_react_component.bind(self).(name, props, options.merge(prerender: false))
    end
  end
end

noisyscanner avatar Apr 19 '19 08:04 noisyscanner

@noisyscanner Thank you for that snippet, that's likely to help a lot of people! Wonder if we could make use of something like that as part of the gem, or at least put it somewhere visible such as readme or wiki page.

BookOfGreg avatar Apr 24 '19 20:04 BookOfGreg

++ on this sentiment. I'm getting this following error:

Encountered error "#<ExecJS::ProgramError: Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.>" when prerendering HelloWorld with {}

which is caused by even just importing this one specific module. Besides the fact that this error message is pretty misleading, it also doesn't print out where this error happened. I might be understanding it wrong, but it looks like something is aggressively catching errors and components are undefined at render time since their module execution failed. Anyone find better ways of debugging prerender issues? Unfortunately I'm not able to provide a fallback of no ssr, since it's a feature that we heavily rely on

avegancafe avatar May 30 '19 14:05 avegancafe

I have a same problem as you @keyboard-clacker -

Encountered error "#<ExecJS::ProgramError: Invariant Violation: Element type is invalid: ...

Error message is wrong - the rendering works for case when it's not prerendered, so it's not the case. It's also quite hard to debug and see where the problem appeared. I suspect it's some dependency on yarn.lock that I've updated, but don't know which and how.

bryszard avatar Jul 11 '19 13:07 bryszard

Regarding Encountered error "#<ExecJS::ProgramError: Invariant Violation: Element type is invalid: ...:

I think one of the core issues is that module lookup uses try...catch. While the errors are logged to the console shim, that typically doesn't help as a later error (such as the invariant violation) will lead to a fatal error (triggering a 500). If that could be refactored to be a bit more intentional based on environment (instead of just reacting based on exceptions, or at the very least, throwing if the caught exception isn't very specific)

EDIT: Here's something you can do to help with the Invariant Violation bug:

In app/javascripts/packs/server_rendering.js, replace ReactRailsUJS.useContext('require.context('components', true)); with

// So that we don't fall back to the global namespace (which leads to hard to
// parse errors).
function fromRequireContext(reqctx) {
  return function(className) {
    const parts = className.split('.');
    const filename = parts.shift();
    const keys = parts;
    // Load the module:
    let component = reqctx('./' + filename);
    // Then access each key:
    keys.forEach((k) => {
      component = component[k];
    });
    // support `export default`
    if (component.__esModule) {
      component = component.default;
    }
    return component;
  };
}
ReactRailsUJS.getConstructor = fromRequireContext(require.context('components', true));

hibachrach avatar Nov 11 '19 07:11 hibachrach

@HarrisonB if we ever happen to meet in person, I owe you a round of drinks. This is exactly what I needed!

avegancafe avatar Nov 14 '19 18:11 avegancafe

@HarrisonB Bro :) saved my life with this script when debugging this 💩 react-rails error :)

adamklepacz avatar Mar 27 '20 12:03 adamklepacz

why this answer isn't the default https://github.com/reactjs/react-rails/issues/264#issuecomment-552326663? The Invariant Violation error doesn't give you any hints on how to debug the error.

orlando avatar Apr 14 '21 23:04 orlando

We have a resolution for this issue. Closing it.

alkesh26 avatar Oct 31 '22 07:10 alkesh26