React-hot-loader: Possible to opt out of the patch at a component/library level?

Created on 1 Dec 2017  Â·  14Comments  Â·  Source: gaearon/react-hot-loader

I’m not sure if this is a react-hot-loader thing or a react-proxy thing, so I’ll start here.

I’m the maintainer of a library for React that consists of a few React components generated by a factory function. These components are causing the following error on first mount:

React Hot Loader: this component is not accepted by Hot Loader. 
Please check is it extracted as a top level class, a function or a variable. 
Click below to reveal the source location: …

The components are class components, but they have no state and their source will never change. I’m assuming that means they don’t need to be wrapped in RHL’s state-preserving magic. As a library author, is it possible to opt out of the wrapper/patch?

bug

Most helpful comment

We will fix it in next release. 🍻

All 14 comments

This message also means, that RHL will not only not-accept this element, but also will remount all the nested tree. So - it is better to fix it, and usually it is not just easy, but may speed up whole application a but.
I checked the sources, and could not found any red flags, like a double-nested HOCs, which may trigger this error.
Could you provide any example, or just an error message?

@theKashey the error occurs in our production application, but I’ll try to get an isolated example that I can share up this weekend.

@meyer - no need of isolated example - any example will be ok. This is the most common problem :( we know how to trace and fix it

@theKashey the class is generated by TypeScript. Not sure if that would cause issues. The transpiled factory function looks like this (with boilerplate omitted):

var cache = getStyleCache();
function factory(displayName, defaultProps) {
  var tagName = 'div';
  return (
    (_a = /** @class */ (function(_super) {
      __extends(JsxstyleComponent, _super);
      function JsxstyleComponent(props) {
        var _this = _super.call(this, props) || this;
        _this.component = props.component || tagName;
        _this.className = cache.getClassName(props, props.className);
        return _this;
      }
      JsxstyleComponent.prototype.componentWillReceiveProps = function(props) {
        this.component = props.component || tagName;
        this.className = cache.getClassName(props, props.className);
      };
      JsxstyleComponent.prototype.render = function() {
        var _a = this.props,
          props = _a.props,
          style = _a.style,
          children = _a.children;
        return createElement(
          this.component,
          __assign({}, props, { className: this.className, style: style }),
          children
        );
      };
      return JsxstyleComponent;
    })(Component)),
    (_a.defaultProps = defaultProps),
    (_a.displayName = displayName),
    _a
  );
  var _a;
}
var Box = factory('Box');
// etc.

We’re using RHL with webpack + the babel plugin:

[
  require.resolve('babel-plugin-transform-class-properties'),
  require.resolve('babel-plugin-transform-object-rest-spread'),
  require.resolve('babel-plugin-syntax-dynamic-import'),
  options.hot && require.resolve('react-hot-loader/babel'),
]

The patch is being included right before the entrypoint file:

[
  options.hot && require.resolve('react-hot-loader/patch'),
  require.resolve('./app'),
]

As far as an example goes, what would work best for you? Code snippets or a working demo?

@meyer - the code looks fine. Could you provide an error message from RHL?

@theKashey here you go:

patch.dev.js:141 React Hot Loader: this component is not accepted by Hot Loader. 
Please check is it extracted as a top level class, a function or a variable. 
Click below to reveal the source location: 
 Ć’ JsxstyleComponent(props) {
        var _this = _super.call(this, props) || this;
        _this.component = props.component || tagName;
        _this.className = cache.getClassName(props, props.classN…

warnAboutUnnacceptedClass @ patch.dev.js:141
resolveType               @ patch.dev.js:160
patchedCreateElement      @ patch.dev.js:182
render                    @ Button.tsx:172
...

@meyer - do you have others calls to factory expect this one? Somewhere in Button.tsx?

@theKashey I don’t. Components are imported from jsxstyle throughout the codebase (including in Button.tsx), but the factory function is called once, when the jsxstyle module is imported. The _first_ import call occurs in Button.tsx, and that’s the source of the error.

Here’s all 100 LOC if you want to take a closer look: https://github.com/smyte/jsxstyle/blob/103c076/packages/jsxstyle/jsxstyle.tsx

Edit: the factory function is called multiple times; could that be the source of the problem?

RHL throws cos it thinks that you are providing some class it should, but could not replace. The reason is simple - the source code the same. Actual implementation depends on the scope variable, but code is the same.
As result - on second element render it will throw.
Should throw on the first render, should not throw on hot module replace. Please check am I right.

There is one thing I could do (and will do) - use displayName to differ Classes.
There is one thing you could do (you better skip) - overload Class.toString to return different codes for different Components.

@theKashey ahhhhhh, I see. yeah, HMR works just fine with this error. It’s mainly a cosmetic thing. The component appears to reload correctly.

We will fix it in next release. 🍻

@meyer - this should be fixed in next version, as long it does not compare classes in that way.
Could you please double check?

@meyer please reopen a new issue if you experienced some problem with React Hot Loader v4.

@neoziro @theKashey I accidentally upgraded from v3 to v4 and have not noticed this issue. I think it’s fixed. Thanks!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Opty1712 picture Opty1712  Â·  4Comments

niba picture niba  Â·  4Comments

JamesIves picture JamesIves  Â·  4Comments

esturcke picture esturcke  Â·  3Comments

zlk89 picture zlk89  Â·  3Comments