react-hot-loader: Invalid 'this' binding with async arrow function class properties
Async arrow functions use an invalid ‘this’ binding when they appear as class properties, as long as the react-hot-loader/babel plugin is present at all.
Arrow functions transformed by react-hot-loader also seem to not respect the spec setting given to the transform-es2015-arrow-functions plugin; however setting spec to false does not help avoid the invalid code either.
I’ve added some snippets of the problematic code, and also some comments annotating both the input and the output.
class Scroller { // not a React component
/* ... */
loadMore = async () => {
const { data } = await api.getAsync(this.nextUrl, null)
this.nextUrl = data.next_url
this.appendData(data)
}
// written to verify if it only affected async
loadMorePromise = () => {
api.getAsync(this.nextUrl, null)
.then(({ data }) => {
this.nextUrl = data.next_url
this.appendData(data)
})
}
}
Snippets of the generated code; in the constructor:
this.loadMorePromise = function () {
// _newArrowCheck and .bind(this) added by the spec: true option
_newArrowCheck(this, _this);
return this.__loadMorePromise__REACT_HOT_LOADER__.apply(this, arguments);
}.bind(this);
this.loadMore = function () {
_newArrowCheck(this, _this);
var _ref2 = _asyncToGenerator(regeneratorRuntime.mark(function _callee2() {
var _args2 = _arguments;
return regeneratorRuntime.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
_context2.next = 2;
return _this.__loadMore__REACT_HOT_LOADER__.apply(_this, _args2);
case 2:
return _context2.abrupt('return', _context2.sent);
case 3:
case 'end':
return _context2.stop();
}
}
}, _callee2, _this);
}));
// wrong Function.name; this one is probably a babel bug
return function NAME(_x2) {
return _ref2.apply(this, arguments);
};
}.bind(this)();
In the _createClass helper’s argument list:
{
key: '__loadMorePromise__REACT_HOT_LOADER__',
value: function __loadMorePromise__REACT_HOT_LOADER__() {
var _this2 = this;
// why is this arrow function not using .bind and _newArrowCheck?
api.getAsync(this.nextUrl, null).then(function (_ref6) {
var data = _ref6.data;
_this2.nextUrl = data.next_url;
_this2.appendData(data);
});
}
}, {
key: '__loadMore__REACT_HOT_LOADER__',
value: function () {
var _ref7 = _asyncToGenerator(regeneratorRuntime.mark(function _callee5() {
var _ref8, data;
return regeneratorRuntime.wrap(function _callee5$(_context5) {
while (1) {
switch (_context5.prev = _context5.next) {
case 0:
_context5.next = 2;
return api.getAsync(_this5.nextUrl, null);
case 2:
_ref8 = _context5.sent;
data = _ref8.data;
// _this5 was not defined anywhere, runtime crash
_this5.nextUrl = data.next_url;
_this5.appendData(data);
case 6:
case 'end':
return _context5.stop();
}
}
}, _callee5, this);
}));
function __loadMore__REACT_HOT_LOADER__() {
return _ref7.apply(this, arguments);
}
return __loadMore__REACT_HOT_LOADER__;
}()
}
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Reactions: 58
- Comments: 30
Commits related to this issue
- Build XDE with Webpack (#17) * [xde] Build XDE with Webpack Build XDE with Webpack. Benefits: - Adds HMR capabilities - Tree shaking with web pack 2 - Potential other optimizations in the future... — committed to expo/xde by skevy 8 years ago
- Fix react-hot-loader version. New version brokes the build: https://github.com/gaearon/react-hot-loader/issues/391 — committed to moira-alert/web2.0 by tihonove 7 years ago
- [xde] Build XDE with Webpack (#17) * [xde] Build XDE with Webpack Build XDE with Webpack. Benefits: - Adds HMR capabilities - Tree shaking with web pack 2 - Potential other optimizations ... — committed to expo/expo-cli by skevy 8 years ago
This is known bug. A solution was found and will come in next version.
+1
I’ve found that the order of the plugins in your
.babelrcmakes a huge difference. For example, the following produces this error:This also gives the error:
However, this works fine:
I found another workaround:
I found a workaround that doesn’t make you to change any code except the method declaration. Just wrap it in IIFE:
This would be definitely fixed in v4 since we do not transpile arrow functions any more.
@wkwiatek is this fixed now? I got this error too. How can I workaround it?
Confirm, it’s fixed in v4
I am escaping this issue with:
or using autobind-decorator
@Kovensky: I took a look at this, and I think it’s two things: there’s a problem with the RHL plugin, where we unnecessarily add
async/awaitto the generated class property. I also think this Babel bug is part of the issue, since we use rest params in the generated code.some problem when upgrade react-hot-loader, in async arrow function, ‘this’ is undefined. I change
foo = async () => { }toasync foo() { }, and it works.This doesn’t work:
However, this works fine with:
I managed to find the root cause of the problem as it appeared for us too.
The problem is strictly connected to the copy of arrow function created as a method in a plugin. However, it’s OK when there’s no
asyncthere.I assume there’s a problem with babel itself rather than methods used there, but there’s a part of code I found harmful for this case: https://github.com/gaearon/react-hot-loader/blob/51dae3f4c86f21c143db838ff5d880433e4bc739/src/babel/index.js#L206
I also created an issue with more details on babel’s repo: https://github.com/babel/babel/issues/5078
And here is the reproduction repository: https://github.com/wkwiatek/babel-async-test
BTW: @calesce, do you need some help on the v3 milestone?
@danielarias123 yeah, but we don’t want people to have to remove
react-hot-loader/babelbecause then stateful components don’t hot-reload 😄It seems that using the alternative config by removing react-hot-loader/babel from .babelrc and adding react-hot-loader/webpack to webpack.config.js works. It is not the same thing but it is good enough for me.
Source: http://gaearon.github.io/react-hot-loader/getstarted/
@andreatosatto90 no, I still use v2 😦 I tried this https://github.com/gaearon/react-hot-loader/issues/391#issuecomment-268638968 but it causes state reload - https://github.com/gaearon/react-hot-loader/issues/642, so I’m just waiting for updates on this issue.
So I was working on a potential fix on
calesce/async-fix.Weirdly enough, if I just run the plugin ahead of time without other Babel transforms and copy the output (using astexplorer), the code runs fine. But if I run it as a normal plugin, it has the broken
_thisbehavior. In both instances I’m usinges2015andstage-2presets.Reproduce project here, I’m kind of stuck on this right now. Anyone else have any ideas?
@valerymercury not currently. it might be worth adding an option to the Babel plugin to opt out of class properties transform.