- Webpacker version: 3.5.5
- React/ReactDOM version: 16.6.3
- Poltergeist version: 1.10.0
- React-Rails version: 1.6.2
- React_UJS version: 2.4.4
PROBLEM:
Running my test build on CircleCI I run into a few difrerent flavors of this --Capybara::Poltergeist::JavascriptError:
A partial look into my .circleci/config.yml
- run:
name: precompile assets (webpacker)
command: NODE_ENV=test bundle exec rake webpacker:compile
Everything in my Chrome browser using the webpack-dev-server looks as it should. Large white space after the first name is just a redacted last name.
When running the build with NODE_ENV=test I get this error in Circle...
Invariant Violation: Minified React error #31; visit https://reactjs.org/docs/error-decoder.html?invariant=31&args[]=object%20with%20keys%20%7B%24%24typeof%2C%20type%2C%20key%2C%20ref%2C%20props%2C%20_owner%2C%20_store%7D&args[]=
(I've looked into this exception and I don't understand it in our context)
Since things spin up fine when running as dev, I decided to toggle the NODE_ENV to development to see what that turned up. The following was the error I got.
TypeError: undefined is not an object (evaluating 'this.__reactAutoBindMap')
Unfortunately there is only one SO post that I could find that mentioned anything about 'this.__reactAutoBindMap' and I didn't find anything on the post helpful at all. (TypeError: undefined is not an object (evaluating 'this.__reactAutoBindMap'))
Procfile
web: bundle exec rails s
webpacker: yarn start
package.json
{
"dependencies": {
"@babel/polyfill": "^7.0.0",
"@rails/webpacker": "3.5",
"babel-preset-react": "^6.24.1",
"caniuse-lite": "^1.0.30000697",
"jquery": "^3.3.1",
"prop-types": "^15.6.2",
"rails-erb-loader": "^5.5.0",
"react": "^16.6.3",
"react-dom": "^16.6.3",
"react_ujs": "^2.4.4",
"webpack": "^3.0.0"
},
"devDependencies": {
"@babel/cli": "^7.2.0",
"@babel/core": "^7.2.0",
"webpack-dev-server": "2.11.2"
},
"scripts": {
"start": "./bin/webpack-dev-server"
}
}
.babelrc
{
"presets": [
[
"env",
{
"modules": false,
"targets": {
"browsers": ["last 2 versions", "safari >= 7"],
"uglify": true
},
"useBuiltIns": true
}
],
"react"
],
"plugins": [
"syntax-dynamic-import",
"transform-object-rest-spread",
[
"transform-class-properties",
{
"spec": true
}
]
]
}
webpacker.yml
Note: You must restart bin/webpack-dev-server for changes to take effect
default: &default
source_path: app/javascript
source_entry_path: packs
public_output_path: packs
cache_path: tmp/cache/webpacker
# Additional paths webpack should lookup modules
# ['app/assets', 'engine/foo/app/assets']
resolved_paths: ['app/javascript/react_16_components']
# Reload manifest.json on all requests so we reload latest compiled packs
cache_manifest: false
extensions:
- .jsx
- .js
- .js.erb
- .sass
- .scss
- .css
- .module.sass
- .module.scss
- .module.css
- .png
- .svg
- .gif
- .jpeg
- .jpg
development:
<<: *default
compile: true
# Reference: https://webpack.js.org/configuration/dev-server/
dev_server:
https: false
host: localhost
port: 3035
public: localhost:3035
hmr: false
# Inline should be set to true if using HMR
inline: true
overlay: true
compress: true
disable_host_check: true
use_local_ip: false
quiet: false
headers:
'Access-Control-Allow-Origin': '*'
watch_options:
ignored: /node_modules/
test:
<<: *default
compile: true
# Compile test packs to a separate directory
public_output_path: packs-test
production:
<<: *default
# Production depends on precompilation of packs prior to booting for performance.
compile: false
# Cache manifest.json for performance
cache_manifest: true
app/javascript/packs/application.js
/* eslint no-console:0 */
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.
//
// To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate
// layout file, like app/views/layouts/application.html.erb
import "@babel/polyfill";
const react16Components = require.context('react_16_components', true);
const ReactRailsUJS = require('react_ujs');
const $ = require('jquery');
ReactRailsUJS.useContext(react16Components);
// Generates and exposes Rails named URLs within React components!
// -- Restarting the server as you would when adding a new Rails route should expose it to the JS world, too.
require('../rails-js-routes/js-routes');
react_test.html.haml (current_user is just a User object)
= react_component 'admin/hello', visitor: current_user
Components:
app/javascript/react_16_components/admin/hello.jsx
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Nav from '../navigation/nav'
export default class Hello extends Component {
static propTypes = {
visitor: PropTypes.object.isRequired
};
seeingRed() {
$('#color-here').css('color', 'red');
}
render() {
const { visitor } = this.props;
return(
<div style={{'textAlign': 'center'}}>
<Nav visitor={visitor.full_name} />
<div>Lets hope webpack gives us minimal trouble!</div>
<button id='color-here' onClick={this.seeingRed}>Click Here!</button>
</div>
);
}
}
app/javascript/react_16_components/navigation/nav.jsx
import React from 'react'
const Nav = ({visitor}) => (
<div><h3>{`H! ${visitor} -- I'm a stateless component navigation bar! I swear (⌐■_■)`}</h3></div>
);
export default Nav;
app/javascript/rails-js-routes/js-routes.js.erb
<%# encoding: UTF-8 %>
<%= JsRoutes.generate() %>
app/config/webpack/loaders/erb.js
module.exports = {
test: /\.erb$/,
enforce: 'pre',
exclude: /node_modules/,
use: [{
loader: 'rails-erb-loader',
options: {
runner: (/^win/.test(process.platform) ? 'ruby ' : '') + 'bin/rails runner'
}
}]
}
app/config/webpack/environment.js
const { environment } = require('@rails/webpacker');
const erb = require('./loaders/erb');
environment.loaders.append('erb', erb);
module.exports = environment;
app/config/webpack/test.js
process.env.NODE_ENV = process.env.NODE_ENV || 'development'
const environment = require('./environment')
module.exports = environment.toWebpackConfig()
CONTEXT:
We're trying to migrate our large application with lots of JS written in CoffeeScript to a new version of React. Instead of bumping the version and working through the deprecation warnings within the React-Rails gem, I'm trying to make it so that we temporarily have two versions of React running and so that each time a fellow engineer touches an old and outdated CoffeeScript file, they use react-codemod and other tools to translate the code into a new React 16 file (and put it in a new directory tree to be consumed, polyfilled/transpiled, and compiled by Webpack instead of Sprockets). I'm still not certain if this is possible, but I'm able to run things fine in local development, so there's hope (famous last words). I'm also able to render an old React 0.14 component via = react_component() using a camelCase class name on top of a React 16 component using the same helper method but with a path argument instead of a class name on the same page, without console error.
We have weird dependencies on jQuery and our Rails named routes being exposed to our React code because we use React in a rather unconventional way (inherited). We've peppered it into our app here and there and didn't just exposed a Rails API so that we could use ReactRouter and Axios (or other tooling) to render/fetch/manipulate data in a more React-ish way.
I find my little understanding and the somewhat 'magic' of Webpacker's conventions obfuscating things for me. I'd almost rather roll my own Webpack config instead of using the gem, but for the sake of hoping I made a rather dumb error (besides trying to use two versions of React) I wanted to put this out there to see if anyone could help shed some light. Super new to Babel and Webpack, so go easy on me -- but would LOVE anyones help.
Thank you in advance.
