react-rails
react-rails copied to clipboard
Cannot find component (Rails 5.1.2, webpacker, react-rails)
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. - [x] 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:
- Rolling back uglifier to 3.0
- Moving the
ReactRailsUJS
both above and belowimport "turbolinks"
andjavascript_include_tag "application.js"
in my mainviews/layouts/application.html.haml
, and removing/addingReactRailsUJS.detectEvents()
inapp/javascript/packs/application.js
. - Adding
console.log(componentRequireContext.keys())
above theReactRailsUJS
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.
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?
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.
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!
Yep – here's my Chrome console:
window.componentRequireContext("./PerformanceRow.jsx").default
// -> function PerformanceRow() {
// _classCallCheck(this, PerformanceRow);
// [... more babel-compiled code...]
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.)
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?
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?
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
}
?
Yep, that makes sense to me. In the call to reqctx
, right?
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
inreact_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 😬
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.
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.
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.
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.
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 🤔
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?
@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.
@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: https://github.com/rails/webpacker/issues/1818#issuecomment-576428973
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.
@ecbrodie Did you end up solving that? I am in the same situation.
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.
@ecbrodie That's true, thanks for the update.
@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 ?
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.