Blueprint: @HotkeysTarget TypeError: class constructors must be invoked with |new|

Created on 16 Jun 2019  路  34Comments  路  Source: palantir/blueprint

Environment

  • __Package version(s)__: "@blueprintjs/core": "^3.15.1", "typescript": "3.5.1"
  • __Browser and OS versions__: Ubuntu 18.04, FireFox 67.0.2

Steps to reproduce

import { Hotkey, Hotkeys, HotkeysTarget } from "@blueprintjs/core";
@HotkeysTarget
export class MyComponent extends React.PureComponent<{}> {
     public render () { return <div>Hello there</div> };
     public renderHotkeys() {
        return <Hotkeys>
            <Hotkey
                combo="up"
                label="Up"
                onKeyDown={() => { alert('Need to go up'); } }
            />
        </Hotkeys>;
    }    
}

Actual behavior

The component cannot be rendered. I get an error that starts with this:

TypeError: class constructors must be invoked with |new|
HotkeysTargetClass
node_modules/@blueprintjs/core/lib/esm/components/hotkeys/hotkeysTarget.js:32

  29 | tslib_1.__extends(HotkeysTargetClass, _super);
  30 | 
  31 | function HotkeysTargetClass() {
> 32 |   return _super !== null && _super.apply(this, arguments) || this;
     | ^  33 | }
  34 | 
  35 | HotkeysTargetClass.prototype.componentWillMount = function () {

Full traceback available, but probably not needed.

Expected behavior

The component should be rendered and the hotkey should be working.

tooling core bug

Most helpful comment

I met the same error (but I dont use TS, just ES6 + webpack + babel).

HotkeysTargetClass() { 
    return _super !== null && _super.apply(this, arguments) || this; 
}

This is a tanspiled to ES5 HotkeysTargetClass class constuctor. The problem is that it expects _super (which is WrappedComponent actually) to be a function (ES5-style class). If in your code WrappedComponent is a ES6 class, it throws an error.

I solved it by using packages/core/lib/esnext/components/hotkeys/hotkeysTarget.js instead of packages/core/lib/esm/components/hotkeys/hotkeysTarget.js, because HotkeysTargetClass is declared in modern way there:

return _a = class HotkeysTargetClass extends WrappedComponent {
   ...
}

All 34 comments

Can you reproduce this in a code sandbox?

I have created an exampe project:

https://github.com/nagylzs/bug3604

You can recreate the problem by:

[email protected]:nagylzs/bug3604.git
cd bug3604
yarn install
yarn run start

Also tried to create a code sandbox example. It does not throw an error!

https://codesandbox.io/s/blueprint-sandbox-tkrkk

But the bug3604 project is really buggy. I'm not sure what is the difference between them. It might be an incompatibility between a create-react-app dependency and blueprintjs? But I doubt it.

I have tried the same example project on a different computer. I got a sightly different error message:

Uncaught TypeError: Class constructor App cannot be invoked without 'new'
    at new HotkeysTargetClass (hotkeysTarget.js:32)
    at constructClassInstance (react-dom.development.js:11786)
    at updateClassComponent (react-dom.development.js:15264)
    at beginWork (react-dom.development.js:16262)
    at performUnitOfWork (react-dom.development.js:20279)
    at workLoop (react-dom.development.js:20320)
    at HTMLUnknownElement.callCallback (react-dom.development.js:147)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:196)
    at invokeGuardedCallback (react-dom.development.js:250)
    at replayUnitOfWork (react-dom.development.js:19503)
    at renderRoot (react-dom.development.js:20433)
    at performWorkOnRoot (react-dom.development.js:21357)
    at performWork (react-dom.development.js:21267)
    at performSyncWork (react-dom.development.js:21241)
    at requestWork (react-dom.development.js:21096)
    at scheduleWork (react-dom.development.js:20909)
    at scheduleRootUpdate (react-dom.development.js:21604)
    at updateContainerAtExpirationTime (react-dom.development.js:21630)
    at updateContainer (react-dom.development.js:21698)
    at ReactRoot.push../node_modules/react-dom/cjs/react-dom.development.js.ReactRoot.render (react-dom.development.js:22011)
    at react-dom.development.js:22163
    at unbatchedUpdates (react-dom.development.js:21486)
    at legacyRenderSubtreeIntoContainer (react-dom.development.js:22159)
    at Object.render (react-dom.development.js:22234)
    at Module../src/index.tsx (index.tsx:7)
    at __webpack_require__ (bootstrap:781)
    at fn (bootstrap:149)
    at Object.0 (serviceWorker.ts:143)
    at __webpack_require__ (bootstrap:781)
    at checkDeferredModules (bootstrap:45)
    at Array.webpackJsonpCallback [as push] (bootstrap:32)
    at main.chunk.js:1

index.js:1375 The above error occurred in the <HotkeysTarget(App)> component:
    in HotkeysTarget(App) (at src/index.tsx:7)

This is the transpiled line that is causing an error:

    function HotkeysTargetClass() {
      return _super !== null && _super.apply(this, arguments) || this;
    }

It seems that _super is a class, and it cannot be "applied" without the new keyword.

I have no idea why this problem is not coming out on code sandbox, but it surely does not work with a default "create-react-app --typescript" project.

@nagylzs I think I have a problem like you
when I changed tsconfig.json compilerOptions.target to es5 the problem is solved

I have the same issue, even if set compilerOptions.target to es5 :

hotkeysTarget.js:32 Uncaught TypeError: Class constructor HotKeyRoot cannot be invoked without 'new'
at new HotkeysTargetClass (hotkeysTarget.js:32)
at constructClassInstance (react-dom.development.js:11786)
at updateClassComponent (react-dom.development.js:15264)
at beginWork (react-dom.development.js:16262)
at performUnitOfWork (react-dom.development.js:20279)
at workLoop (react-dom.development.js:20320)
at HTMLUnknownElement.callCallback (react-dom.development.js:147)
...

I also use create-react-app --typescript for this project.

@sgaloux I second you.

Used create-react-app --typescript in my project, I checked I have the same blueprint and react versions as the codesandbox thing, yet I get the error in my project.

Also having es5 as target.

experiencing same issue with cra 3 and typescript changing target to es5 doesn't help.

Here is the repo with failing decorator https://github.com/kresli/cra3-bp3-decorator-error

I met the same error (but I dont use TS, just ES6 + webpack + babel).

HotkeysTargetClass() { 
    return _super !== null && _super.apply(this, arguments) || this; 
}

This is a tanspiled to ES5 HotkeysTargetClass class constuctor. The problem is that it expects _super (which is WrappedComponent actually) to be a function (ES5-style class). If in your code WrappedComponent is a ES6 class, it throws an error.

I solved it by using packages/core/lib/esnext/components/hotkeys/hotkeysTarget.js instead of packages/core/lib/esm/components/hotkeys/hotkeysTarget.js, because HotkeysTargetClass is declared in modern way there:

return _a = class HotkeysTargetClass extends WrappedComponent {
   ...
}

Thanks @alxmiron it took my a while to find your workaround (which is working so far)
But now my question is: how to do this properly?
Is there a way to "tell" blueprintjs to use the esnext modules?
Usually I don't want to manually modify stuff in the node_modules folder

Thanks @alxmiron it took my a while to find your workaround (which is working so far)
But now my question is: how to do this properly?
Is there a way to "tell" blueprintjs to use the esnext modules?
Usually I don't want to manually modify stuff in the node_modules folder

Easy. Change default resolve.mainVields webpack config value

const webpackConfig = {
  resolve {
    mainFields: ['esnext', 'browser', 'module', 'main']
  }
}

But be careful, it will look for esnext target in other modules too, not only blueprintjs. This is recommended if you support only evergreen browsers

This was fixed by https://github.com/palantir/blueprint/pull/3230.

Also @alxmiron it can be safe to add esnext to resolve.mainFields even if you support older browsers as long as you are using babel-loader _and_ you are babeling your dependencies. Check out this blog post from the babel folks on their thoughts towards babeling deps.

An alternative solution to #3230 would be to create something like a hook or a higher order component that gives you the same functionality as @HotkeysTarget without using the class decorator. This would avoid the need to edit your webpack config's resolve.mainFields since it would avoid the need for blueprint to instantiate your class component.

I started suddenly to have the same problem. The strange thing is that I cannot reproduce it when I build the application (CRA + typescript) myself, but the CI build has the issue. I tried several versions of typescript / blueprint / react (I first thought that bumping to the latest version caused the problem but it didn't).

Maybe this has to do with node version. I'm using 13.7.0 locally (no issue) but CI uses older versions (one env uses 8.x, the other I'm not sure).

Edit: I applied the same workaround as in commit just above and it did the trick 馃憤

I'm having exactly this problem as well. I'm not really a JS developer, I'm a Java developer who is muddling my way though some react code and I don't really understand how any of this stuff works. I am just using plain JS (no typescript) and the app was originally created with create-react-app so there is a package.json file but no webpack config.
I don't know how to apply any of the above work-arounds, can anyone help? Is there something I can do in my package.json file?

create-react-app does use a webpack config, its just not editable unless you "eject". Would recommend trying this workaround right now if you don't feel comfortable diving into js build tooling right now: https://github.com/pmclachlan/blink-mind/commit/79511a52ac379ab04f4d7fa96588733bac54b8cd

Thankyou @maclockard - that worked for me.

I have same error using Babel + Webpack without ts. Is there any ways to fix it?

@adidahiya is there an official fix coming for this? Both of the two solutions mentioned above are really workarounds not true fixes. Also I've tried both and neither is working for me unfortunately :(

Does anyone know what caused this issue to start showing up? What changed exactly, the blueprintjs code or babel or webpack?

Thanks!

@tnrich if you are curious, the reason why this started happening is that more people starting using es6, which has native classes. The underlying issue is that typescript's shim for classes when targeting es5 are not fully compatible with es6 classes (specifically instantiating es6 classes from an es5 shim class).

Also, if neither solution above didn't work for you, it wouldn't surprise me if you are running into a different issue preventing HotkeysTarget form working than the es5/es6 incompatibility. mind posting some error information?

@maclockard none of these 2 solutions work for me =(

Actually I see a very strange error log

Uncaught exception TypeError: Class constructor Filter cannot be invoked without 'new'
    at new HotkeysTarget(Filter) (/Users/nikolaykupstas/Projects/bus-kit/frontend/node_modules/@blueprintjs/core/src/components/hotkeys/hotkeysTarget.tsx:41:12)

@Kupstas could you include a code snippet showing how you are importing and using HotkeysTarget using the second solution?

@maclockard here's what I see when I tried:

import {HotkeysTarget} from '@blueprintjs/core/lib/esnext/components/hotkeys/hotkeysTarget.js';
 ERROR  in ./node_modules/@blueprintjs/core/lib/esnext/components/button/buttons.js 29:38
Module parse failed: Unexpected token (29:38)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
|             : (ref) => {
|                 this.buttonRef = ref;
>                 this.props.elementRef?.(ref);
|             };
|     }
 @ ./node_modules/@blueprintjs/core/lib/esnext/components/toast/toast.js 24:0-57 48:36-42 72:39-51
 @ ./node_modules/@blueprintjs/core/lib/esnext/components/index.js
 @ ./node_modules/@blueprintjs/core/lib/esnext/components/hotkeys/hotkeysDialog.js
 @ ./node_modules/@blueprintjs/core/lib/esnext/components/hotkeys/hotkeysEvents.js
 @ ./node_modules/@blueprintjs/core/lib/esnext/components/hotkeys/hotkeysTarget.js
 @ ./src/withEditorInteractions/createSequenceInputPopup.js
 @ ./src/withEditorInteractions/index.js
 @ ./src/index.js
 @ ./demo/src/index.js
 @ multi ./node_modules/@pmmmwh/react-refresh-webpack-plugin/src/runtime/ReactRefreshEntry.js (webpack)-dev-server/client?http://localhost:3344/ ./node_modules/@pmmmwh/react-refresh-webpack-plugin/src/runtime/ErrorOverlayEntry.js (webpack)/hot/only-dev-server.js ./demo/src/index.js

 ERROR  in ./node_modules/@blueprintjs/core/lib/esnext/components/forms/asyncControllableInput.js 45:42
Module parse failed: Unexpected token (45:42)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
|                 localValue: this.state.externalValue,
|             });
>             this.props.onCompositionStart?.(e);
|         };
|         this.handleCompositionEnd = (e) => {
 @ ./node_modules/@blueprintjs/core/lib/esnext/components/forms/inputGroup.js 24:0-66 50:32-54
 @ ./node_modules/@blueprintjs/core/lib/esnext/components/index.js
 @ ./node_modules/@blueprintjs/core/lib/esnext/components/hotkeys/hotkeysDialog.js
 @ ./node_modules/@blueprintjs/core/lib/esnext/components/hotkeys/hotkeysEvents.js
 @ ./node_modules/@blueprintjs/core/lib/esnext/components/hotkeys/hotkeysTarget.js
 @ ./src/withEditorInteractions/createSequenceInputPopup.js
 @ ./src/withEditorInteractions/index.js
 @ ./src/index.js
 @ ./demo/src/index.js
 @ multi ./node_modules/@pmmmwh/react-refresh-webpack-plugin/src/runtime/ReactRefreshEntry.js (webpack)-dev-server/client?http://localhost:3344/ ./node_modules/@pmmmwh/react-refresh-webpack-plugin/src/runtime/ErrorOverlayEntry.js (webpack)/hot/only-dev-server.js ./demo/src/index.js

 ERROR  in ./node_modules/@blueprintjs/core/lib/esnext/components/forms/numericInput.js 54:58
Module parse failed: Unexpected token (54:58)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
|             shouldSelectAfterUpdate: false,
|             stepMaxPrecision: NumericInput_1.getStepMaxPrecision(this.props),
>             value: getValueOrEmptyValue(this.props.value ?? this.props.defaultValue),
|         };
|         // updating these flags need not trigger re-renders, so don't include them in this.state.
 @ ./node_modules/@blueprintjs/core/lib/esnext/components/index.js 38:0-37 38:0-37
 @ ./node_modules/@blueprintjs/core/lib/esnext/components/hotkeys/hotkeysDialog.js
 @ ./node_modules/@blueprintjs/core/lib/esnext/components/hotkeys/hotkeysEvents.js
 @ ./node_modules/@blueprintjs/core/lib/esnext/components/hotkeys/hotkeysTarget.js
 @ ./src/withEditorInteractions/createSequenceInputPopup.js
 @ ./src/withEditorInteractions/index.js
 @ ./src/index.js
 @ ./demo/src/index.js
 @ multi ./node_modules/@pmmmwh/react-refresh-webpack-plugin/src/runtime/ReactRefreshEntry.js (webpack)-dev-server/client?http://localhost:3344/ ./node_modules/@pmmmwh/react-refresh-webpack-plugin/src/runtime/ErrorOverlayEntry.js (webpack)/hot/only-dev-server.js ./demo/src/index.js

I got similar looking webpack errors when trying the "mainFields" solution.

I added like in example. But I have got an error. Something like Cannot use import statement outside a module. Because I needed a fast solution, I removed HotkeysTarget and used another library

Also I had the same errors like @tnrich by using first solution

@tnrich the issue you are seeing there is due to the fact something in you loader chain can't handle the optional chain operator being used by blueprint when targeting esnext. As per this comment, https://github.com/palantir/blueprint/issues/3604#issuecomment-551288942 you need to both be using babel-loader _and_ be babeling blueprint (or all upstream dependencies). If you are currently doing this and still seeing that error, make sure that the versions of babel you are resolving is at least 7.8.0. This is the version that babel-parser added support for optional chaining. (Make sure that its all the babel deps since they share a version number!).

There's a chance that create react app by default is babeling upstream deps already for you, if that's the case then all you need to do is fix what version of babel is being resolved.

For webpack, to babel dependencies/blueprint just change the include/exclude option of the relevant rule config for the babel loader.

@Kupstas you could try using require instead of import, since it sounds like you don't have es modules or typescript setup.

Also, a third possible solution here instead of using the esnext target version of blueprint is to make it so that your build targets es5 or change your browserlist such that it makes babel polyfill classes instead of using native classes.

In my personal opinion, I think that an ideal fix would be HOC versions of HotkeysTarget and ContextMenuTarget, and to possibly deprecate the existing decorator versions. This would remove any need to change configs to use them and would be more 'reactish'. Using class decorators across a module boundary gets really messy if one side is polyfilling classes. Hooks may also be a good fit here, but I think blueprint supports react 15, so that would be a no go.

Also, I might be wrong about babel being the thing that's failing to parse the optional chain operator, its just usually been the culprit in the past. If its still giving that error after making sure that blueprint is being babel and making sure that babel is at least 7.8.0, the parse error could be a different loader in your loader chain. In that case, try bumping the versions of these loaders and/or of webpack to see if later versions support optional chaining.

In my personal opinion, I think that an ideal fix would be HOC versions of HotkeysTarget and ContextMenuTarget, and to possibly deprecate the existing decorator versions. This would remove any need to change configs to use them and would be more 'reactish'. Using class decorators across a module boundary gets really messy if one side is polyfilling classes. Hooks may also be a good fit here, but I think blueprint supports react 15, so that would be a no go.

This seems like the best solution to me as well. I use nwb on top of webpack and don't feel like digging into the babel stuff right now so just downgraded nwb to a previous version where everything was working. This is fine for the time being but it would be great to have a simple HOC solution.

Why we cannot just render HotKey inside of view?

Any movement on this one @adidahiya @maclockard ? Thanks!

Some thoughts on this: Create React App does not support decorators by default, see here.

I propose just moving away from decorators for now, as many libraries (e.g. MobX) have done, or at least adding another option for users to declare HotKeys, maybe a React hook.

This issue has been plaguing me for quite a while now. I fully support what @alexkreidler suggests which is to provide another option for this functionality that doesn't require decorators support which can be quite finicky to get tooling to support. In my case it works when using blueprint as a regular requirement but not when I try to link blueprint locally as a webpack alias (helpful for local development https://medium.com/@tnrich_29519/a-better-alternative-to-npm-yarn-link-for-the-front-end-667f03d497a6).

I'm working on this as part of Blueprint v4.0 changes, it's the next thing on my list, should be able to prototype something later this week.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

raiju picture raiju  路  3Comments

tgreenwatts picture tgreenwatts  路  3Comments

brsanthu picture brsanthu  路  3Comments

ernestofreyreg picture ernestofreyreg  路  3Comments

giladgray picture giladgray  路  3Comments