Skip to content

Cannot find component (Rails 5.1.2, webpacker, react-rails) #758

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
1 of 4 tasks
andrewcsmith opened this issue Jul 31, 2017 · 23 comments
Closed
1 of 4 tasks

Cannot find component (Rails 5.1.2, webpacker, react-rails) #758

andrewcsmith opened this issue Jul 31, 2017 · 23 comments

Comments

@andrewcsmith
Copy link

Help us help you! Please choose one:

  • My app crashes with react-rails, so I've included the stack trace and the exact steps which make it crash.
  • My app doesn't crash, but I'm getting unexpected behavior. So, I've described the unexpected behavior and suggested a new behavior.
  • I'm trying to use react-rails with another library, but I'm having trouble. I've described my JavaScript management setup (eg, Sprockets, Webpack...), how I'm trying to use this other library, and why it's not working.
  • I have another issue to discuss.

Essentially the same resulting problem as #757 and #713, where including my (haml) view helper = react_component 'PerformanceRow' gives me the following error messages:

Error: Cannot find module './PerformanceRow'.
    at webpackContextResolve (application.js:4549)
    at webpackContext (application.js:4544)
    at eval (fromRequireContext.js?f05c:13)
    at Object.eval [as getConstructor] (fromRequireContextWithGlobalFallback.js?7c97:13)
    at Object.mountComponents (index.js?0542:82)
    at HTMLDocument.ReactRailsUJS.handleMount (index.js?0542:124)
    at HTMLDocument.dispatch (event.js?268c:338)
    at HTMLDocument.elemData.handle (event.js?268c:146)

fromRequireContextWithGlobalFallback.js?7c97:20 ReferenceError: PerformanceRow is not defined
    at eval (eval at module.exports (fromGlobal.js?7c2e:22), <anonymous>:1:1)
    at module.exports (fromGlobal.js?7c2e:13)
    at Object.eval [as getConstructor] (fromRequireContextWithGlobalFallback.js?7c97:17)
    at Object.mountComponents (index.js?0542:82)
    at HTMLDocument.ReactRailsUJS.handleMount (index.js?0542:124)
    at HTMLDocument.dispatch (event.js?268c:338)
    at HTMLDocument.elemData.handle (event.js?268c:146)

index.js?0542:89 [react-rails] Cannot find component: 'PerformanceRow' for element <div data-react-class=​"PerformanceRow" data-react-props=​"{"id":​5}​">​</div>​

index.js?0542:91 Uncaught Error: Cannot find component: 'PerformanceRow'. Make sure your component is available to render.
    at Object.mountComponents (index.js?0542:91)
    at HTMLDocument.ReactRailsUJS.handleMount (index.js?0542:124)
    at HTMLDocument.dispatch (event.js?268c:338)
    at HTMLDocument.elemData.handle (event.js?268c:146)

I've tried the following:

  1. Rolling back uglifier to 3.0
  2. Moving the ReactRailsUJS both above and below import "turbolinks" and javascript_include_tag "application.js" in my main views/layouts/application.html.haml, and removing/adding ReactRailsUJS.detectEvents() in app/javascript/packs/application.js.
  3. Adding console.log(componentRequireContext.keys()) above the ReactRailsUJS initializer, which prints ["./PerformanceRow.jsx"] in my Chrome console.

Here's my directory structure:

app/javascript/packs/
├── application.js
├── hello_react.jsx
└── server_rendering.js

app/javascript/components/
└── PerformanceRow.jsx

And some code:

// app/javascript/packs/application.js

import ReactRailsUJS from 'react_ujs'

// Support component names relative to this directory:
var componentRequireContext = require.context("components", true, /\.jsx?$/)
// => prints ["./PerformanceRow.jsx"] in Chrome console
console.log(componentRequireContext.keys());
ReactRailsUJS.useContext(componentRequireContext)
// app/javascript/components/PerformanceRow.jsx

import React, { Component } from "react"
import ReactDOM from "react-dom"

class PerformanceRow extends Component {
  render() {
    return (
      <tr className="performance">
        <td>HEY THERE</td>
        <td />
        <td />
        <td>Delete</td>
      </tr>
    )
  }
}

export default PerformanceRow;
-# Extract from app/views/performances/_embedded_form.html.haml
    = react_component 'PerformanceRow'

As far as I can tell, I've double-checked capitalization and name-mangling, and componentRequireContext can definitely find the correct file. However, PerformanceRow still seems to be unreachable. I can also access ReactRailsUJS in the global context of the Chrome console.

I should also mention that the default hello_react.jsx works just fine, but that the problem here seems to lie in ReactRailsUJS and the use of the components/ directory. Tried to trim things as best I could, but let me know if I need to include any other information.

@rmosolgo
Copy link
Member

Hi! Thanks for the great bug report.

What if you add .jsx to your react_component call, for example:

react_component 'PerformanceRow.jsx'

?

I think webpack does some magic with the .js file extension, but I bet that it works differently with .jsx, does that work any better?

@andrewcsmith
Copy link
Author

Thanks for the quick reply!

That does not work. It changes the last error to "Cannot find component: 'PerformanceRow.jsx'" but otherwise it's the same. I also tried running PerformanceRow.jsx through babeljs.io and saving it to app/javascript/components/PerformanceRow.js but that didn't solve it either.

@rmosolgo
Copy link
Member

Can you try accessing the componentRequireContext directly to make sure it's working?

Add to packs/application.js

// Attach it to the window: 
window.componentRequireContext = componentRequireContext

Then in Chrome console, access the globally-available componentRequireContext and try to require the module:

window.componentRequireContext 
// => should return the require context 
window.componentRequireContext.require("./PerformanceRow")
// does that return the react component? 

If that doesn't return the react component, try a few variations, just in case a file extension might help.

If that does return the component, then there must be some error in react-rails code which is messing up that lookup!

@andrewcsmith
Copy link
Author

Yep – here's my Chrome console:

window.componentRequireContext("./PerformanceRow.jsx").default
// -> function PerformanceRow() {
//        _classCallCheck(this, PerformanceRow);
//        [... more babel-compiled code...]

@andrewcsmith
Copy link
Author

Oh, however, I do have to add the extension to get window.componentRequireContext to work.

The original error message from the helper is Cannot find module './PerformanceRow'., and when I try window.ComponentRequireContext("./PerformanceRow") I get the exact same error message.

This leads me to believe that, somewhere, react-rails is translating react_component 'PerformanceRow' into a call to the module without any extension. When I try react_component 'PerformanceRow.jsx', it thinks that .jsx is a member of module PerformanceRow and still doesn't correct the filename. (That's just my assumption - might be something else of course.)

@rmosolgo
Copy link
Member

it thinks that .jsx is a member of module PerformanceRow

you're so right about that!

Here's that lookup code:

https://github.com/reactjs/react-rails/blob/master/react_ujs/src/getConstructor/fromRequireContext.js#L9

I think we could just treat .jsx as a special case, that way .jsx would Just Work. What do you think?

@andrewcsmith
Copy link
Author

I think requiring react_component "MyComponent.jsx" instead of just react_component "MyComponent" could run into confusion (thinking about people copy-pasting from tutorials, stackoverflow, etc), and probably will breed lots of little spammy issues. Is there a way to make react_component "MyComponent" find the .jsx file, so that (most) tutorial code would still work as-written?

@rmosolgo
Copy link
Member

I'm not sure how to tell if the .jsx is needed ahead of time, so the best i can think of is

try {
  // Load by name 
} catch (err) { 
  // Load by name+.jsx 
}

?

@andrewcsmith
Copy link
Author

Yep, that makes sense to me. In the call to reqctx, right?

@rmosolgo
Copy link
Member

yep, that's it! if it throws an error, i guess we just try again with a .jsx.

it's too bad, thinking how that will add some latency to every single page load.

I can think of two ways to address that:

  • Keep an in-memory cache of { whatWasInTheDom => whatWeUsedWithWebpack } so that we can keep track of whether the .jsx was needed or not.
  • Support .jsx in react_component so that perf-conscious users can hardcode it

Or, leave it for later , which is also fine. apparently not too many folks have come across this bug 😬

andrewcsmith added a commit to andrewcsmith/react-rails that referenced this issue Jul 31, 2017
If the original `fileName` search doesn't work, look for the file with `.jsx` on the end. This makes it possible to include `.jsx` files in the `app/javascript/components` directory and have `react-rails` find them automatically, importing their components.
@andrewcsmith
Copy link
Author

Excellent! I've opened a PR #759 to fix this. This fix solves the issue on my local machine.

I agree about the performance thing though. I'm not particularly savvy with js performance optimization, or with the react/webpack pipeline, so I'm not really the one to do that. But thanks for your help and I'm happy to test future things with this same app if needed.

andrewcsmith added a commit to andrewcsmith/react-rails that referenced this issue Aug 20, 2017
@chrisjingram
Copy link

If you have this issue, and the above did not solve it, it may be because you previously installed react-rails to work with the rails asset pipeline, and then installed webpacker later.

Removing //= require react and //= require react_ujs from application.js or application.js.coffee fixed this for me.

I'm assuming that having react and especially react_ujs in the asset pipeline meant that ReactRailsUJS was being set up to expect components also from the asset pipeline, and that setup was taking precedence over the webpacker setup.

@andrewcsmith
Copy link
Author

I installed both at the same time, and didn't have either in the asset pipeline. In the original problem, react-rails was finding components with .js extensions, just not .jsx extensions. Wonder why that would do it.

quidquid referenced this issue in loveland/react-rails Mar 23, 2019
It's the patch from reactjs#759 that was never merged to master, but I still need.
@obedparla
Copy link

obedparla commented Aug 28, 2019

I'm facing the exact same issue at the moment. Had .js files so I changed them to .jsx but having the same issue still. Everything is almost the same as the setup @andrewcsmith had, but I can't fix it by changing the extension. Any ideas, anyone?

It's worth noting that I'm moving from the assets pipeline setup to Webpacker. I moved my components folder from assets/javascript/ to /javascript and removed react and react_ujs from application.js.

@obedparla
Copy link

Update: I was wrong about the above, it does work with .jsx but I was importing the files directly with their name without any folder just as it's done with the asset pipeline, but it seems that with Webpacker (or at least my setup) you need to specify the folder they're on too:

react_component('myfolder/mysubfolder/Component' .....)

This seems a bit odd since I didn't read anything about adding folders in the react-rails documentation 🤔

@nick-bigger
Copy link

nick-bigger commented Oct 10, 2019

Having almost the exact same issue. After following the fixes above, I've hit a dead end. Running console.log(componentRequireContext.keys()); does correctly show the component ["./post.jsx"] However, running window.componentRequireContext.require("./Post.jsx") from the Chrome console throws this error:

*$:10 Uncaught Error: Cannot find module 'Post.jsx'.
    at webpackContextResolve (.*$:10)
    at webpackContext (.*$:5)
    at <anonymous>:1:8

No combination of the component name, file extension, or folder path fixes this issue for me. Any ideas for a way forward?

@BookOfGreg
Copy link
Member

@nbigger can you make an example rails app that displays the problem? I closed PR #759 because nobody involved was able to produce a replication. If you have one then we can finally get this fixed.

@ecbrodie
Copy link

@BookOfGreg I run into this problem when I apply filtering to require.context() to only load production JS/JSX/TS/TSX files in my application pack. The main purpose of me doing this is to have my test files/folders excluded from asset compilation, since they shouldn't be shipped to production. My plight with passing the appropriate filter regex to require.context is documented here: rails/webpacker#1818 (comment)

Unfortunately, I then run into a problem with react-rails, in that my calls to react_component started failing. I have a hunch it is due to directory names no longer being loaded by require.context, but I'm not too sure. Anyway, I hope that this provides you with another angle to think about this problem.

I am happy to delve further into reproduction steps with you.

@akshaysmurthy
Copy link

@ecbrodie Did you end up solving that? I am in the same situation.

@ecbrodie
Copy link

I was forced to adjust my regex to allow directories to be included, as well as the JavaScript/Typescript files themselves:

const componentRequireContext = require.context("components", true, /^(?!.*__tests__\/.*$).+$/)

I am not fond of this solution though, I feel like react-rails should be able to load components by name if the regex excludes directories but loads files.

@akshaysmurthy
Copy link

@ecbrodie That's true, thanks for the update.

@chrisvel
Copy link

@ecbrodie This just worked but I don't understand why it did. I experimented for some hours without any success before trying your solution. Are you excluding the ./ from the component inclusion ?

@ecbrodie
Copy link

I switched jobs last year and no longer work on the project in question, nor work on a project using neither webpacker nor react-rails. Thus I am unable to comment further to this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants