reactjs / react-rails

Integrate React.js with Rails views and controllers, the asset pipeline, or webpacker.
Apache License 2.0
6.75k stars 759 forks source link

Debugging Solutions for Prerender #264

Closed blainekasten closed 2 years ago

blainekasten commented 9 years ago

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

alert('ReactRails encountered an error server side rendering, please inspect your issue with your browser dev tools');
debugger;
// error code is here.

thoughts?

mchristen commented 9 years ago

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.

blainekasten commented 9 years ago

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.

rmosolgo commented 9 years ago

:+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!

barefootford commented 9 years ago

@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.

rmosolgo commented 9 years ago

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.

axhamre commented 9 years ago

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! :)

barefootford commented 9 years ago

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.

yuku commented 9 years ago

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
shaimo commented 8 years ago

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!

tyler-boyd commented 8 years ago

@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.

noisyscanner commented 5 years ago

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
BookOfGreg commented 5 years ago

@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.

avegancafe commented 5 years ago

++ 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

bryszard commented 5 years ago

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.

hibachrach commented 4 years ago

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));
avegancafe commented 4 years ago

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

adamklepacz commented 4 years ago

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

orlando commented 3 years ago

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.

alkesh26 commented 2 years ago

We have a resolution for this issue. Closing it.