Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Webpack HMR Throws React syntheticEvent Error Due To jsPDF

SYSTEM INFO:

OSX 10.12.4 Sierra
Node v7.10.0
npm v4.2.0

BROWSERS TESTED:

Chrome 58.0.3029.110
Safari 10.1
Firefox 53.0

THE PROBLEM:

I have an app running effectively in production that I've cloned and am attempting to update in preparation for building out a sequel. However, I've run into a strange Webpack issue by updating it / React / HMR to more recent builds.

The HMR will connect, and Webpack appears to compile just fine. However, interacting with the page (such as clicking) generates the following error:

syntheticEvent Error

Obviously, the app is at that point no longer functional as clicking things does not fire off anything. Interestingly, we also get the following 404 error in the Node console and on the browser:

404 (NOTE: this appears to be a giant query string incl functions, and specifically referencing syntheticEvent. I can print the whole thing for you if you want)

NODE v6.3.1

RELEVANT NPMs:

"react": "^15.5.4",
"react-dom": "^15.5.4",
"react-transform-catch-errors": "^1.0.0",
"react-transform-hmr": "^1.0.4",

"webpack": "^2.5.1",
"webpack-dev-middleware": "^1.10.2",
"webpack-hot-middleware": "^2.18.0"

"babel-cli": "^6.11.4",
"babel-core": "^6.24.1",
"babel-eslint": "^7.2.1",
"babel-loader": "^7.0.0",
"babel-plugin-array-includes": "^2.0.0",
"babel-plugin-transform-decorators-legacy": "^1.0.0",
"babel-plugin-transform-object-assign": "^6.0.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babel-preset-react-hmre": "^1.1.1",
"babel-preset-stage-1": "^6.24.1",

WEBPACK.CONFIG.DEV.JS:

var path = require('path');
var webpack = require('webpack');
var autoprefixer = require('autoprefixer');
var hotMiddlewareScript = 'webpack-hot-middleware/client';

console.log('using the dev config file');
console.log('THE PUBLIC PATH: ' + path.join(__dirname, '/CLIENTSIDE/static'));

module.exports = {
  devtool: 'eval',
  entry: {
    background: ['webpack-hot-middleware/client', path.join(__dirname, '/CLIENTSIDE/components/background')],
    uniqueShare: ['webpack-hot-middleware/client',  path.join(__dirname, '/CLIENTSIDE/components/uniqueShare')],
    starRating: ['webpack-hot-middleware/client', path.join(__dirname, '/CLIENTSIDE/components/starRating')],
    testingPage: ['webpack-hot-middleware/client', path.join(__dirname, '/CLIENTSIDE/components/testingPage')],
    style: ['webpack-hot-middleware/client', path.join(__dirname, '/CLIENTSIDE/components/style')]
  },
  output: {
    path: path.join(__dirname, '/CLIENTSIDE/static'),
    filename: '[name].js',
    publicPath: '/static/'
  },
  plugins: [
    new webpack.optimize.OccurrenceOrderPlugin(),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoEmitOnErrorsPlugin()
  ],
  module: {
    loaders: [
        {
            test: /\.(js|jsx)$/,
            exclude: /(node_modules|bower_components)/,
            loader: 'babel-loader',
            query: {
              cacheDirectory: true,
              presets: ['react', 'es2015', 'stage-1'],
              plugins: ['transform-decorators-legacy', 'transform-object-assign', 'array-includes'],
            },
        },
        {
            test: /\.scss$/,
            loaders: ['style-loader', 'css-loader', 'sass-loader']
        }
      ]
    }
  };

WHERE WE LOAD UP HMR:

  console.log('****************************** RUNNING IN DEV MODE ******************************');
  var webpack = require('webpack');
  var webpackConfig = require('./webpack.config.dev');
  var compiler = webpack(webpackConfig);

  console.log('Looking for the HMR here: ' + webpackConfig.output.publicPath);

  app.use(require('webpack-dev-middleware')(compiler, {
    noInfo: true,
    publicPath: webpackConfig.output.publicPath
  }));

  app.use(require('webpack-hot-middleware')(compiler));

Now, here's the strange part. Babel / Webpack compile everything just fine in production mode, without the hot reloader. The app runs perfectly fine when we set the Node ENV to 'PRODUCTION' - no syntheticEvent errors.

Furthermore, the app runs in dev mode (with hot reloading) just fine using the previous stack, which includes the following NPM versions:

"react": "^0.14.8",
"react-dom": "^0.14.3",
"react-transform-catch-errors": "^1.0.0",
"react-transform-hmr": "^1.0.0",

"webpack": "^1.13.1",
"webpack-dev-middleware": "^1.2.0",
"webpack-hot-middleware": "^2.0.0"

"babel": "^6.5.2",
"babel-cli": "^6.10.1",
"babel-core": "^6.10.4",
"babel-loader": "^6.2.4",
"babel-plugin-transform-es2015-modules-commonjs": "^6.0.2",
"babel-plugin-transform-react-constant-elements": "^6.0.2",
"babel-preset-es2015": "^6.0.8",
"babel-preset-react": "^6.0.2",

UPDATE 5/16/17:

We've isolated the problem to migration from React / React DOM 0.14.8 to v15.0.0. Doing so immediately triggers our error - but only in HMR / Dev mode.

The production build, without HMR, compiles just fine; referencing the minified files Webpack produces through that config lets the app function 100% normally.

The exact same build runs 100% fine in React / DOM 0.14.8, in Dev mode, with HMR. Upgrading ONLY Webpack / HMR / webpack-hot-middleware to the latest versions does NOT trigger the error.

I spent some time digging into the debug panel and found the following:

Clickeventobject This is a decent glance at the actual event object being generated natively by React. In this case, a "topClick" that is not associated with any handlers we've created. I can literally fire this & the error by clicking anywhere on the page.

Where the error happens This is the line where the error actually occurs. It's coming from react-dom/lib/SyntheticUIEvent.js - and appears to have failed to initialize a .call method on the SyntheticEvent class... enter image description here

It's also worth noting that the console also fires the following error every time you reload, first thing.

GET http://localhost:3333/[object%20Object]?url=function%20SyntheticEvent(dispa…d%20%3D%20emptyFunction.thatReturnsFalse%3B%0A%20%20return%20this%3B%0A%7D 404 (Not Found)
(anonymous) @ jspdf.debug.js:17350
l @ jspdf.min.js:284
u @ jspdf.min.js:284
XHR @ jspdf.debug.js:17334
Proxy @ jspdf.debug.js:16928
(anonymous) @ SyntheticEvent.js:188
(anonymous) @ SyntheticEvent.js:268
(anonymous) @ background.js:805
__webpack_require__ @ background.js:658
fn @ background.js:86
(anonymous) @ SyntheticCompositionEvent.js:13
(anonymous) @ background.js:2110
__webpack_require__ @ background.js:658
fn @ background.js:86
(anonymous) @ BeforeInputEventPlugin.js:16
(anonymous) @ background.js:1767
__webpack_require__ @ background.js:658
fn @ background.js:86
(anonymous) @ ReactDefaultInjection.js:14
(anonymous) @ background.js:1963
__webpack_require__ @ background.js:658
fn @ background.js:86
(anonymous) @ ReactDOM.js:16
(anonymous) @ ReactDOM.js:111
(anonymous) @ background.js:1844
__webpack_require__ @ background.js:658
fn @ background.js:86
(anonymous) @ index.js:3
(anonymous) @ background.js:812
__webpack_require__ @ background.js:658
fn @ background.js:86
(anonymous) @ background.js:9
(anonymous) @ background.js:3178
__webpack_require__ @ background.js:658
fn @ background.js:86
(anonymous) @ background:2
(anonymous) @ background.js:3287
__webpack_require__ @ background.js:658
(anonymous) @ background.js:707
(anonymous) @ background.js:710

The same error from the Node console:

GET /[object%20Object]?url=function%20SyntheticEvent(dispatchConfig%2C%20targetInst%2C%20nativeEvent%2C%20nativeEventTarget)%20%7B%0A%20%20if%20(process.env.NODE_ENV%20!%3D%3D%20%27production%27)%20%7B%0A%20%20%20%20%2F%2F%20these%20have%20a%20getter%2Fsetter%20for%20warnings%0A%20%20%20%20delete%20this.nativeEvent%3B%0A%20%20%20%20delete%20this.preventDefault%3B%0A%20%20%20%20delete%20this.stopPropagation%3B%0A%20%20%7D%0A%0A%20%20this.dispatchConfig%20%3D%20dispatchConfig%3B%0A%20%20this._targetInst%20%3D%20targetInst%3B%0A%20%20this.nativeEvent%20%3D%20nativeEvent%3B%0A%0A%20%20var%20Interface%20%3D%20this.constructor.Interface%3B%0A%20%20for%20(var%20propName%20in%20Interface)%20%7B%0A%20%20%20%20if%20(!Interface.hasOwnProperty(propName))%20%7B%0A%20%20%20%20%20%20continue%3B%0A%20%20%20%20%7D%0A%20%20%20%20if%20(process.env.NODE_ENV%20!%3D%3D%20%27production%27)%20%7B%0A%20%20%20%20%20%20delete%20this%5BpropName%5D%3B%20%2F%2F%20this%20has%20a%20getter%2Fsetter%20for%20warnings%0A%20%20%20%20%7D%0A%20%20%20%20var%20normalize%20%3D%20Interface%5BpropName%5D%3B%0A%20%20%20%20if%20(normalize)%20%7B%0A%20%20%20%20%20%20this%5BpropName%5D%20%3D%20normalize(nativeEvent)%3B%0A%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20if%20(propName%20%3D%3D%3D%20%27target%27)%20%7B%0A%20%20%20%20%20%20%20%20this.target%20%3D%20nativeEventTarget%3B%0A%20%20%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20this%5BpropName%5D%20%3D%20nativeEvent%5BpropName%5D%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%0A%20%20var%20defaultPrevented%20%3D%20nativeEvent.defaultPrevented%20!%3D%20null%20%3F%20nativeEvent.defaultPrevented%20%3A%20nativeEvent.returnValue%20%3D%3D%3D%20false%3B%0A%20%20if%20(defaultPrevented)%20%7B%0A%20%20%20%20this.isDefaultPrevented%20%3D%20emptyFunction.thatReturnsTrue%3B%0A%20%20%7D%20else%20%7B%0A%20%20%20%20this.isDefaultPrevented%20%3D%20emptyFunction.thatReturnsFalse%3B%0A%20%20%7D%0A%20%20this.isPropagationStopped%20%3D%20emptyFunction.thatReturnsFalse%3B%0A%20%20return%20this%3B%0A%7D 404 4.745 ms - 35162
like image 421
Zfalen Avatar asked May 15 '17 16:05

Zfalen


1 Answers

UPDATE 5/24/17:

We finally figured out the issue - a classic mistake!

After retooling our entire stack to be more in line with create-react-app, I migrated our prior React app into the dev server Index file. Worked as expected, albeit without the right HTML wrapping and styles etc.

However, when I migrated the full HTML page to which we render out the app to a <div id="root> element, it started spitting out the SyntheticEvent error again! A lot of work and restructuring finally isolated the problem to the HTML file (which, realistically, was actually an EJS file originally and mostly contains a basic skeleton as well as SEO tracking)

Here's the rub: we had an outdated <script/> tag that was calling out to jsPDF v1.2.61 (https://github.com/MrRio/jsPDF). We are no longer actually using this plugin and the script appears to have snuck its way back in to our code due to a merge conflict.

In retrospect, the code posted in the OP does reference the jsPDF plugin, but the stack trace doesn't really provide any indication of where the error originates. Because we literally haven't touched the plugin since the very beginning of the app, and pursued an alternative approach, the name didn't ring any bells - given the later calls to React's scripts, I simply assumed the first three or so lines of the stack trace indicated jspdf was some obscure part of the React library.

THERE ARE TWO FIXES TO THIS ISSUE:

  1. Remove the script entirely - this is what we did, as we no longer are using jsPDF in our application.
  2. Update jsPDF - the plugin is now at v1.3.4. The prior version, v1.2.61, generates a breaking conflict with React v15.0.0+ native library scripts. The new version DOES NOT generate conflicts.

Hopefully this will help somebody down the road!

Reference only; prior solution that did not work and tipped us off to the fix above:

In order to move our project forward, we've opted for an alternative stack using create-react-app and custom-react-scripts.

https://github.com/facebookincubator/create-react-app

https://github.com/kitze/custom-react-scripts

Ultimately, we are (loosely) following this pattern to run the CRA Stack alongside our Node / Express stuff concurrently: https://www.fullstackreact.com/articles/using-create-react-app-with-a-server/

At the end of the day, this accomplishes our goal of pushing the new stack into up-to-date technologies. And, it's probably a little cleaner than what we were running before.

However, I am still completely mystified as to the origin of the error above. I've moved stuff around in a bunch of other test stacks and can't seem to replicate the issue w/Webpack 2 and React 15+ - and there's nothing fundamentally different about the way I am setting them up from our original stack.

Whatever is going on, it's very strange.

like image 173
Zfalen Avatar answered Oct 07 '22 10:10

Zfalen