Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Variable assignment while object destructuring not working properly 'Error: "action" is read-only.' #10193

Closed
pwnreza opened this issue Jul 10, 2019 · 14 comments

Comments

Projects
None yet
3 participants
@pwnreza
Copy link

commented Jul 10, 2019

Bug Report

Current Behavior
We have a promise rejection in our react-native app.
It is possible to log it thru logcat (android logs).
It is regarding following code: See Input Code

When I try to "overload" the 'someAction' variable I get an runtime error which I can inspect via logcat (android logs used on my react-native app) looking something like this:

Possible Unhandled Promise Rejection (id: 1): Error: "someAction" is read-only.

If I don't "overload" the variable and instead try to do something like this
componentWillMount() { this.props.someAction(); }
it will work.

I guess it has something to do with how the babel helper handles the arguments and/or the export/imports.
But I am not sure at all.

Input Code

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { someAction } from '../../store/data/actions';

class SomeComponent extends Component {
  componentWillMount() {
    this.foo();
  }

  async foo() {
    const { someAction } = this.props;
    return await someAction();
  }

  render() {
    return null;
  }
}

export default connect(null, {
  someAction,
})(SomeComponent);

Expected behavior/code
Expected behavior would be that I can use a variable name inside my class/component (i.e. getting my action out of my props attribute by destructuring), even tho the variable name is also taken by the import of the action in the file.

Babel Configuration (.babelrc, package.json, cli command)

{
  "presets": ["module:metro-react-native-babel-preset"],
  "env": {
    "production": {
      "plugins": ["transform-remove-console"]
    }
  }
}

    "@babel/runtime": "^7.5.4",
    "babel-plugin-transform-remove-console": "^6.9.4",
    "metro-react-native-babel-preset": "^0.55.0",

Environment

  • Babel version(s): 7.5.4
  • Node/npm version: Node v10.15.3 / Npm 6.4.1
  • OS: OSX 10.14.5
  • How you are using Babel: react-native

Additional context/Screenshots
Babel transpiles the code to something like this:

__d(function(global, _$$_REQUIRE, _$$_IMPORT_DEFAULT, _$$_IMPORT_ALL, module, exports, _dependencyMap) {
    var _interopRequireDefault = _$$_REQUIRE(_dependencyMap[0], "@babel/runtime/helpers/interopRequireDefault");
    var _interopRequireWildcard = _$$_REQUIRE(_dependencyMap[1], "@babel/runtime/helpers/interopRequireWildcard");
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.default = void 0;
    var _regenerator = _interopRequireDefault(_$$_REQUIRE(_dependencyMap[2], "@babel/runtime/regenerator"));
    var _classCallCheck2 = _interopRequireDefault(_$$_REQUIRE(_dependencyMap[3], "@babel/runtime/helpers/classCallCheck"));
    var _createClass2 = _interopRequireDefault(_$$_REQUIRE(_dependencyMap[4], "@babel/runtime/helpers/createClass"));
    var _possibleConstructorReturn2 = _interopRequireDefault(_$$_REQUIRE(_dependencyMap[5], "@babel/runtime/helpers/possibleConstructorReturn"));
    var _getPrototypeOf2 = _interopRequireDefault(_$$_REQUIRE(_dependencyMap[6], "@babel/runtime/helpers/getPrototypeOf"));
    var _inherits2 = _interopRequireDefault(_$$_REQUIRE(_dependencyMap[7], "@babel/runtime/helpers/inherits"));
    var _react = _interopRequireWildcard(_$$_REQUIRE(_dependencyMap[8], "react"));
    var _index = _interopRequireDefault(_$$_REQUIRE(_dependencyMap[9], "./../../../node_modules/react-transform-hmr/lib/index.js"));
    var _reactRedux = _$$_REQUIRE(_dependencyMap[10], "react-redux");
    var _actions = _$$_REQUIRE(_dependencyMap[12], "../../store/data/actions");
    var _services = _$$_REQUIRE(_dependencyMap[15], "../../services");
    var _jsxFileName = "/somePath/node_modules/react-native/app/components/SomeComponent/SomeComponent.js";
    var _components = {
        SomeComponent: {
            displayName: "SomeComponent"
        }
    };
    var _node_modulesReactTransformHmrLibIndexJs2 = (0, _index.default)({
        filename: "/somePath/node_modules/react-native/app/components/SomeComponent/SomeComponent.js",
        components: _components,
        locals: [module],
        imports: [_react.default]
    });

    function _wrapComponent(id) {
        return function(Component) {
            return _node_modulesReactTransformHmrLibIndexJs2(Component, id);
        };
    }
    var SomeComponent = function(_Component) {
        (0, _inherits2.default)(SomeComponent, _Component);

        function SomeComponent() {
            (0, _classCallCheck2.default)(this, SomeComponent);
            return (0, _possibleConstructorReturn2.default)(this, (0, _getPrototypeOf2.default)(SomeComponent).apply(this, arguments));
        }(0, _createClass2.default)(SomeComponent, [{
            key: "componentWillMount",
            value: function componentWillMount() {
                this.foo();
            }
        }, {
            key: "foo",
            value: function foo() {
                var _this$props, someAction, _ref, type;
                return _regenerator.default.async(function foo$(_context) {
                    while (1) {
                        switch (_context.prev = _context.next) {
                            case 0:
                                _this$props = this.props, _actions.someAction = (_this$props.someAction, function() {
                                    throw new Error('"' + "someAction" + '" is read-only.');
                                }());
                                return _regenerator.default.awrap((0, _actions.someAction)());
                            case "end":
                                return _context.stop();
                        }
                    }
                }, null, this);
            }
        }, {
            key: "render",
            value: function render() {
                return null;
            }
        }]);
        return SomeComponent;
    }(_react.Component);
    SomeComponent = _wrapComponent("SomeComponent")(SomeComponent);
    var _default2 = (0, _reactRedux.connect)(null, {
        someAction: _actions.someAction,
    })(SomeComponent);
    exports.default = _default2;
}, 1365, [1, 544, 208, 114, 115, 122, 123, 125, 137, 255, 539, 575, 581, 956, 543, 584], "app/components/SomeComponent/SomeComponent.js");
@babel-bot

This comment has been minimized.

Copy link
Collaborator

commented Jul 10, 2019

Hey @pwnreza! We really appreciate you taking the time to report an issue. The collaborators
on this project attempt to help as many people as possible, but we're a limited number of volunteers,
so it's possible this won't be addressed swiftly.

If you need any help, or just have general Babel or JavaScript questions, we have a vibrant Slack
community
that typically always has someone willing to help. You can sign-up here
for an invite.

@pwnreza

This comment has been minimized.

Copy link
Author

commented Jul 10, 2019

This is how babel transpiles the same code when you use this.props.someAction() instead of getting it with object destructering first:

 case 0:
              _regenerator.default.awrap(this.props.someAction());

you can see that the hard-coded errorthrow is missing

@pwnreza pwnreza changed the title Variable assignment while object destructuring not working properly Variable assignment while object destructuring not working properly 'Error: "action" is read-only.' Jul 10, 2019

@nicolo-ribaudo

This comment has been minimized.

Copy link
Member

commented Jul 11, 2019

Minimal example:

// babel.config.js

module.exports = {
  plugins: [
    "@babel/plugin-transform-destructuring",
    "@babel/plugin-transform-regenerator",
    "@babel/plugin-transform-classes",
    "@babel/plugin-transform-runtime",
    "@babel/plugin-transform-modules-commonjs"
  ]
};
// input.js

import { someAction } from 'actions';

class SomeComponent {
  async foo() {
    const { someAction } = bar;
  }
}
@pwnreza

This comment has been minimized.

Copy link
Author

commented Jul 11, 2019

@nicolo-ribaudo
So you could reproduce it?

@nicolo-ribaudo

This comment has been minimized.

Copy link
Member

commented Jul 11, 2019

Yes, I'll try to fix it in the following days

@pwnreza

This comment has been minimized.

Copy link
Author

commented Jul 11, 2019

@nicolo-ribaudo
Thank you very much <3
Its pretty urgent because we are live and cannot build for production with updated packages.

@pwnreza

This comment has been minimized.

Copy link
Author

commented Jul 15, 2019

Are there any updates regarding this Issue?
We haven't found a hotfix for this problem yet.
So my team and I are pretty dependent on some sort of solution for this bug

thanks for your help

@nicolo-ribaudo

This comment has been minimized.

Copy link
Member

commented Jul 15, 2019

As a workaround (if you are using yarn) you can add this to your package.json:

  "resolutions": {
    "regenerator-transform": "~0.13.0"
  }

If you are using npm, installing it as a devDependency, deleting package-lock and re-installing your dependencies might work.

@pwnreza

This comment has been minimized.

Copy link
Author

commented Jul 16, 2019

now its rolling, great!

The hotfix did not work yesterday. But could be a mistake in sync the android project for react-native, too.
I will try the hotfix again today and give an update

@pwnreza

This comment has been minimized.

Copy link
Author

commented Jul 18, 2019

I hadn't time to try it the new version.

I did now. Sadly it is not working.

Set my package version as following:
"@babel/runtime": "7.5.5"

But the same error as mentioned above gets thrown in the code which got transpiled by Babel.

It is still trying to assign the function which is gathered by object destructuring, to the helper object in which the imported function with the same name is stored

@pwnreza

This comment has been minimized.

Copy link
Author

commented Jul 18, 2019

@nicolo-ribaudo
Sry, I just saw in the newest version wasn't even related to the fix of this issue. Am I right?

@nicolo-ribaudo

This comment has been minimized.

Copy link
Member

commented Jul 19, 2019

Yes, you need to wait for a new regenerator version (don't need to wait for a Babel version then).
Doesn't #10193 (comment) work?

@pwnreza

This comment has been minimized.

Copy link
Author

commented Jul 19, 2019

@nicolo-ribaudo thank you so much. You helped us a lot!!!

@nicolo-ribaudo

This comment has been minimized.

Copy link
Member

commented Jul 19, 2019

Fixed by facebook/regenerator#377

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.