2

I'm trying to set up react-rails for my app (https://github.com/reactjs/react-rails).

I had no problem getting it to set up initially, but I'm anticipating a lot of different individual components so I want to organize my /javascript/components folder neatly.

So I have something like this

components
 character
   avatar-selector
     AvatarSelector.tsx
 HomeLogo.tsx

The AvatarSelector.tsx exports the component as default.

And I have

const componentRequireContext = require.context("components", true);
const ReactRailsUJS = require("react_ujs");
ReactRailsUJS.useContext(componentRequireContext);

in both packs/application.js and packs/server-rendering.js

When I render the HomeLogo component everything works fine both with and without prerender. When I then go to render the component with react_component('character/avatar-selector/AvatarSelector', avatar_props, prerender: false) it also works fine.

But when I switch to prerender: true it gives an error.

ExecJS::ProgramError: Error: 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 character/avatar-selector/AvatarSelector

I have already tried directly adding

const AvatarSelector = require('../components/character/avatar-selector/AvatarSelector')

to my packs and trying to render the component as AvatarSelector but I still get the same error.

Almaron
  • 4,127
  • 6
  • 26
  • 48

1 Answers1

0
 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));

Reference - https://github.com/reactjs/react-rails/issues/264

Kannan G
  • 974
  • 6
  • 9