Coder Social home page Coder Social logo

autobind-decorator's Introduction

autobind decorator

A class or method decorator which binds methods to the instance so this is always correct, even when the method is detached.

This is particularly useful for situations like React components, where you often pass methods as event handlers and would otherwise need to .bind(this).

// Before:
<button onClick={ this.handleClick.bind(this) }></button>

// After:
<button onClick={ this.handleClick }></button>

As decorators are a part of future ECMAScript standard they can only be used with transpilers such as Babel.

Installation

npm install autobind-decorator

Supported platforms

Output

We target IE11+ browsers (see out browserslist) with the following caveats:

main: ES5

module: ES5 + ES modules to enable tree shaking

es: modern JS

On consuming modern JS, you can transpile the script to your target environment (@babel/preset-env is recommended) to minimise the cost. For more details, please read https://babeljs.io/blog/2018/06/26/on-consuming-and-publishing-es2015+-packages.

Dev

node 8.10+ with latest npm

Babel 6 users (legacy only)

The implementation of the decorator transform is currently on hold as the syntax is not final. If you would like to use this project with Babel 6, you may use babel-plugin-transform-decorators-legacy which implement Babel 5 decorator transform for Babel 6.

Babel 7 users

Legacy

Babel 7's @babel/plugin-proposal-decorators officially supports the same logic that babel-plugin-transform-decorators-legacy has, but integrates better with Babel 7's other plugins. You can enable this with

{
  "plugins": [
    ["@babel/plugin-proposal-decorators", { "legacy": true }],
  ]
}

in your Babel configuration. Note that legacy: true is specifically needed if you want to get the same behavior as transform-decorators-legacy because there are newer versions of the decorator specification coming out, and they do not behave the same way.

Modern

For now, you'll have to use one of the solutions in https://github.com/nicolo-ribaudo/legacy-decorators-migration-utility. We are trying to keep this module up-to-date with the latest spec. For more details, please read https://babeljs.io/blog/2018/09/17/decorators.

TypeScript users

This package will work out of the box with TypeScript (no Babel needed) and includes the .d.ts typings along with it.

Examples

Recommended way to bind a method

Use @boundMethod on a method

import {boundMethod} from 'autobind-decorator'

class Component {
  constructor(value) {
    this.value = value
  }

  @boundMethod
  method() {
    return this.value
  }
}

let component = new Component(42)
let method = component.method // .bind(component) isn't needed!
method() // returns 42

@boundMethod makes method into an auto-bound method, replacing the explicit bind call later.

Discouraged approaches

Magical @autobind that can be used on both classes and methods

import autobind from 'autobind-decorator'

class Component {
  constructor(value) {
    this.value = value
  }

  @autobind
  method() {
    return this.value
  }
}

let component = new Component(42)
let method = component.method // .bind(component) isn't needed!
method() // returns 42

// Also usable on the class to bind all methods
// Please see performance section below if you decide to autobind your class
@autobind
class Component { }

Use @boundClass on a class

Please see performance section below if you decide to autobind your class

import {boundClass} from 'autobind-decorator'

@boundClass
class Component {
  constructor(value) {
    this.value = value
  }

  method() {
    return this.value
  }
}

let component = new Component(42)
let method = component.method // .bind(component) isn't needed!
method() // returns 42

Performance

autobind (boundMethod) on a method is lazy and is only bound once. πŸ‘

However,

It is unnecessary to do that to every function. This is just as bad as autobinding (on a class). You only need to bind functions that you pass around. e.g. onClick={this.doSomething}. Or fetch.then(this.handleDone) -- Dan Abramov‏

You should avoid using autobind (boundClass) on a class. πŸ‘Ž

I was the guy who came up with autobinding in older Reacts and I'm glad to see it gone. It might save you a few keystrokes but it allocates functions that'll never be called in 90% of cases and has noticeable performance degradation. Getting rid of autobinding is a good thing -- Peter Hunt

Alternative

autobind-decorator's People

Contributors

acrazing avatar aldendaniels avatar andreypopp avatar blaremc avatar chentsulin avatar dependabot[bot] avatar domarmstrong avatar gaearon avatar jakeboone02 avatar kesha-antonov avatar mnbuyskih avatar pelotom avatar roblabla avatar stevemao avatar tbrunel avatar volodymyrkr avatar xapphire13 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

autobind-decorator's Issues

Failed to minify the code from /autobind-decorator/src/index.js:7

I have an error when runyarn build. I've tried what @khelkun suggested here but it doesn't work for me. I've also tried to edit autobind-decorator to 2.1.1 but it also doesn't work.

This issue was described here but the ticket is closed without a solution so I create this ticket.

$ yarn build
yarn run v1.12.3
$ node scripts/build.js

> [email protected] build /path/myapp/client
> react-scripts build

Creating an optimized production build...
Failed to compile.

Failed to minify the code from this file: 

    ./node_modules/autobind-decorator/src/index.js:7 

Read more here: https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#npm-run-build-fails-to-minify

npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] build: `react-scripts build`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the [email protected] build script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /path/.npm/_logs/2018-11-15T22_23_20_735Z-debug.log
✨  Done in 11.02s.

Here's my package.json:

"scripts": {
    "server": "node server.js",
    "client": "node scripts/start-client.js",
    "start": "concurrently \"nodemon server.js\" \"npm run client\"",
    "build": "node scripts/build.js"
  },

My build.js and start-client.js inside ./scripts:

// build.js
const args = ["run build"];
const opts = { stdio: "inherit", cwd: "client", shell: true };
require("child_process").spawn("npm", args, opts);

// start-client.js
const args = ["start"];
const opts = { stdio: "inherit", cwd: "client", shell: true };
require("child_process").spawn("npm", args, opts);

I'm not able to use this autobind decorator this with TypeScript

Just looked at #37 too, but neither this:

import autobind from 'autobind-decorator'

Nor this:

import * as autobind from 'autobind-decorator'

Works for me... The first complains about a missing default export, the second gives me this:

Module ''autobind-decorator'' resolves to a non-module entity and cannot be imported using this construct.

Am I doing something wrong?

1.4.1? export = autobind breaks our builds

Please have a look as fast as possible. We have downgraded to 1.4.0

============================================
Showing 1 changed file with 1 addition and 1 deletion.
View
2 index.d.ts
@@ -1,4 +1,4 @@
declare module 'autobind-decorator' {
const autobind: ClassDecorator & MethodDecorator;

  • export default autobind;
  • export = autobind;
    }
    ============================================

Autobind not working with inheritance

In the following example, executing the autobound function removes the overridden method. Executing it again will no longer call the overridden function, and only call the base class.

window.A = class A {
    regular() {
        console.log("A.regular");
    }
    @autobind autobound() {
        console.log("A.autobound");
    }
};
window.B = class B extends window.A {
    regular() {
        console.log("B.regular");
        super.regular();
    }
    autobound() {
        console.log("B.autobound");
        super.autobound();
    }
};
window.a = new window.A();
window.b = new window.B();
b.regular();b.regular(); // B.regular A.regular B.regular A.regular 
b.autobound();b.autobound(); // B.autobound A.autobound A.autobound 

This is using version 2.4.0

dist file `module.exports` overrides `exports['default']`

Hi there, it seems like the dist file exports both module.exports & exports['default'] and the former is overwriting the later. I try building this locally and don't see that problem. Maybe bc of dependencies are out of date while building before publishing?

My local build

'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

exports.default = autobind;
/**
 * @copyright 2015, Andrey Popp <[email protected]>
 *
 * The decorator may be used on classes or methods
 * ```
 * @autobind
 * class FullBound {}
 *
 * class PartBound {
 *   @autobind
 *   method () {}
 * }
 * ```
 */
function autobind() {
  if (arguments.length === 1) {
    return boundClass.apply(undefined, arguments);
  } else {
    return boundMethod.apply(undefined, arguments);
  }
}

/**
 * Use boundMethod to bind all methods on the target.prototype
 */
function boundClass(target) {
  // (Using reflect to get all keys including symbols)
  var keys = void 0;
  // Use Reflect if exists
  if (typeof Reflect !== 'undefined' && typeof Reflect.ownKeys === 'function') {
    keys = Reflect.ownKeys(target.prototype);
  } else {
    keys = Object.getOwnPropertyNames(target.prototype);
    // use symbols if support is provided
    if (typeof Object.getOwnPropertySymbols === 'function') {
      keys = keys.concat(Object.getOwnPropertySymbols(target.prototype));
    }
  }

  keys.forEach(function (key) {
    // Ignore special case target method
    if (key === 'constructor') {
      return;
    }

    var descriptor = Object.getOwnPropertyDescriptor(target.prototype, key);

    // Only methods need binding
    if (typeof descriptor.value === 'function') {
      Object.defineProperty(target.prototype, key, boundMethod(target, key, descriptor));
    }
  });
  return target;
}

/**
 * Return a descriptor removing the value and returning a getter
 * The getter will return a .bind version of the function
 * and memoize the result against a symbol on the instance
 */
function boundMethod(target, key, descriptor) {
  var fn = descriptor.value;

  if (typeof fn !== 'function') {
    throw new Error('@autobind decorator can only be applied to methods not: ' + (typeof fn === 'undefined' ? 'undefined' : _typeof(fn)));
  }

  // In IE11 calling Object.defineProperty has a side-effect of evaluating the
  // getter for the property which is being replaced. This causes infinite
  // recursion and an "Out of stack space" error.
  var definingProperty = false;

  return {
    configurable: true,
    get: function get() {
      if (definingProperty || this === target.prototype || this.hasOwnProperty(key)) {
        return fn;
      }

      var boundFn = fn.bind(this);
      definingProperty = true;
      Object.defineProperty(this, key, {
        value: boundFn,
        configurable: true,
        writable: true
      });
      definingProperty = false;
      return boundFn;
    }
  };
}

1.4.3:

/**
 * @copyright 2015, Andrey Popp <[email protected]>
 *
 * The decorator may be used on classes or methods
 * ```
 * @autobind
 * class FullBound {}
 *
 * class PartBound {
 *   @autobind
 *   method () {}
 * }
 * ```
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});
exports['default'] = autobind;

function autobind() {
  for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
    args[_key] = arguments[_key];
  }

  if (args.length === 1) {
    return boundClass.apply(undefined, args);
  } else {
    return boundMethod.apply(undefined, args);
  }
}

/**
 * Use boundMethod to bind all methods on the target.prototype
 */
function boundClass(target) {
  // (Using reflect to get all keys including symbols)
  var keys = undefined;
  // Use Reflect if exists
  if (typeof Reflect !== 'undefined' && typeof Reflect.ownKeys === 'function') {
    keys = Reflect.ownKeys(target.prototype);
  } else {
    keys = Object.getOwnPropertyNames(target.prototype);
    // use symbols if support is provided
    if (typeof Object.getOwnPropertySymbols === 'function') {
      keys = keys.concat(Object.getOwnPropertySymbols(target.prototype));
    }
  }

  keys.forEach(function (key) {
    // Ignore special case target method
    if (key === 'constructor') {
      return;
    }

    var descriptor = Object.getOwnPropertyDescriptor(target.prototype, key);

    // Only methods need binding
    if (typeof descriptor.value === 'function') {
      Object.defineProperty(target.prototype, key, boundMethod(target, key, descriptor));
    }
  });
  return target;
}

/**
 * Return a descriptor removing the value and returning a getter
 * The getter will return a .bind version of the function
 * and memoize the result against a symbol on the instance
 */
function boundMethod(target, key, descriptor) {
  var fn = descriptor.value;

  if (typeof fn !== 'function') {
    throw new Error('@autobind decorator can only be applied to methods not: ' + typeof fn);
  }

  // In IE11 calling Object.defineProperty has a side-effect of evaluating the
  // getter for the property which is being replaced. This causes infinite
  // recursion and an "Out of stack space" error.
  var definingProperty = false;

  return {
    configurable: true,
    get: function get() {
      if (definingProperty || this === target.prototype || this.hasOwnProperty(key)) {
        return fn;
      }

      var boundFn = fn.bind(this);
      definingProperty = true;
      Object.defineProperty(this, key, {
        value: boundFn,
        configurable: true,
        writable: true
      });
      definingProperty = false;
      return boundFn;
    }
  };
}
module.exports = exports['default'];

Uncaught TypeError: decorator is not a function

With the latest release 1.4.4, we get an error when using the autobind on a method, and building with webpack and babel-plugin-transform-decorators-legacy:

class Suggestion extends PureComponent {
    @autobind
    _onClick() {
        return this.props.onClick(this.props.suggestion);
    }
    ...
}

inside _applyDecoratedDescriptor (from webpack plugin) ->

Uncaught TypeError: decorator is not a function

How to pass params?

Before:
<button onClick={ this.handleClick.bind(this, key) }></button>
After:
<button onClick={ this.handleClick(key) }></button> //Not working

v2.2 breaks create-react-app v1

The changes made in v2.2 cause the following error running npm run build with a react-scripts 1.1 (aka "Create React App"):

Creating an optimized production build...
Failed to compile.

Failed to minify the code from this file:

        ./node_modules/autobind-decorator/src/index.js:7

The above issue went away after downgrading autobind-decorator to 2.1.0.

I'm mostly just posting this here as an FYI in case someone else runs into this...

Thoughts around using ES7 property initializers instead?

Good morning, and thank you for this decorator - the binding of methods on components has been messy and this does a nice job in helping us clean it up.

Some of my teammates had shared this and I have been encouraging them to use ES7 Property Initializers and arrow functions instead of a decorator like this. We write a lot of React code, it has made sense to drop the dependencies and using the language to help us achieve our goals.

https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#es7-property-initializers

In addition, we have a new bind operator - https://github.com/tc39/proposal-bind-operator which creates a more elegant solution for dealing with context control.

What are your thoughts?

Uglify errors in react scripts

I am running into an Uglify error that was traced to [email protected]

[email protected] works fine. The following is the error:

Failed to compile.

Failed to minify the bundle. Error: static/js/main.d366c2d2.js from UglifyJs
Unexpected token: name (fn) [static/js/main.d366c2d2.js:396674,6]
    at compiler.run (/Users/ugangha/Sandbox/Pearson/Cite/greenville/node_modules/@pearson-incubator/react-scripts/scripts/build.js:130:23)
    at emitRecords.err (/Users/ugangha/Sandbox/Pearson/Cite/greenville/node_modules/webpack/lib/Compiler.js:269:13)
    at Compiler.emitRecords (/Users/ugangha/Sandbox/Pearson/Cite/greenville/node_modules/webpack/lib/Compiler.js:375:38)
    at emitAssets.err (/Users/ugangha/Sandbox/Pearson/Cite/greenville/node_modules/webpack/lib/Compiler.js:262:10)
    at applyPluginsAsyncSeries1.err (/Users/ugangha/Sandbox/Pearson/Cite/greenville/node_modules/webpack/lib/Compiler.js:368:12)
    at next (/Users/ugangha/Sandbox/Pearson/Cite/greenville/node_modules/tapable/lib/Tapable.js:218:11)
    at Compiler.compiler.plugin (/Users/ugangha/Sandbox/Pearson/Cite/greenville/node_modules/webpack/lib/performance/SizeLimitsPlugin.js:99:4)
    at next (/Users/ugangha/Sandbox/Pearson/Cite/greenville/node_modules/tapable/lib/Tapable.js:220:14)
    at /Users/ugangha/Sandbox/Pearson/Cite/greenville/node_modules/sw-precache-webpack-plugin/lib/index.js:98:18
    at <anonymous>
Read more here: http://bit.ly/2tRViJ9

This error is happening in a "let" statement. From what I read I feel Uglify cannot handle ES6 code and create react scripts does not convert all node modules to ES5 before feeding to Uglify. My assumption is some build option is changed from 2.1.0 to 2.2.1 so that the build is provided in ES6 not pure ES5.

"Cannot set property" error on React class component method

I'm guessing I'm missing something but not sure what.

My .babelrc:

{
  "presets": ["react", "es2015", "stage-0"],

  "plugins": [
    "transform-decorators-legacy"
  ],

  "env": {
    "development": {
      "plugins": [ "typecheck" ]
    }
  }
}

My webpack loader config:

{
    test: /\.jsx?$/,
    exclude: /node_modules/,
    loaders: [ 'react-hot', 'babel?presets[]=es2015&presets[]=react&presets[]=stage-0&plugins[]=transform-decorators-legacy' ]
}

My .eslintrc (I have no idea if both of these are needed):

  "parser": "babel-eslint",
  "settings": {
    "import/parser": "babel-eslint",

In my React component, I use the autobind decorator:

import autobind from 'autobind-decorator';

export default class Home extends Component {

    @autobind
    onPress() {}

}

But I get the runtime error:

makeAssimilatePrototype.js:21 Uncaught TypeError: Cannot set property onPress of # which has only a getter

Not working well with react-native 0.53.3

I use @autobind for methods in class.

Autobind works well for the first time the component is mounted.
But the second time it is mounted it seems to be bound to the old instance of this.

When I remove @autobind and use usual constructor bind statements all is working as expected.

Has anyone experienced a similar issue? Any suggestions?

Does not work with Babel 6.0

I have not managed to run autobind decorator with babelify 7.2.0 and babel 6.0.15, the lasts versions.
I don't have errors but i think that autobind is not made, I have to make it for the hand.

My configuration in gulp:

...
transform: [babelify.configure({
      presets: ["react", "stage-0", "es2015"],
    })],
...

My bad ?

[React JS] Method has decorators, put the decorator plugin before the classes one

I tried to use this in React js

import React from 'react';
import autobind from 'autobind-decorator';

class Login extends React.Component {
    @autobind
    focus() {
        this.textInput.focus();
    }

    render() {
        return (
           <div>
                 <input type="text" ref={ (input) => {this.textInput = input} }/>
                 <input type="button" value="Focus the text input" onClick={this.focus} />
           </div>
        );
     }
}

It show error

Method has decorators, put the decorator plugin before the classes one

Please help me.

{
  "name": "react-test",
  "private": true,
  "scripts": {
    "start": "meteor run"
  },
  "dependencies": {
    "autobind-decorator": "^1.3.4",
    "babel-runtime": "^6.18.0",
    "bootstrap": "^3.3.7",
    "core-decorators": "^0.15.0",
    "graphql": "^0.9.1",
    "griddle-react": "^0.7.1",
    "jquery-validation": "^1.16.0",
    "meteor-node-stubs": "~0.2.3",
    "react": "^15.4.2",
    "react-addons-pure-render-mixin": "^15.4.2",
    "react-bootstrap": "^0.30.7",
    "react-breadcrumbs": "^1.5.2",
    "react-dom": "^15.4.2",
    "react-router": "^3.0.2",
    "react-router-bootstrap": "^0.23.1",
    "simpl-schema": "^0.1.0",
    "uniforms": "^1.9.0",
    "uniforms-bootstrap3": "^1.9.0"
  },
  "devDependencies": {}
}

boundMethod decorator might not be usable in Angular production builds

I get this error when running production builds of my Angular app, which contains usages of the boundMethod decorator:

 Error encountered resolving symbol values statically. Only initialized variables and constants can be referenced because the value of this variable is needed by the template compiler (position 3:22 in the original .ts file), resolving symbol boundMethod in C:/Users/Jeff/[path to my app]/node_modules/autobind-decorator/index.d.ts

I believe it is the Angular AoT compiler which is throwing this error. Devleopment builds, which don't use AoT, don't produce these errors.

I import the decorator using import { boundMethod } from "autobind-decorator";

A workaround is to use the autobind decorator instead, though that is discouraged by this package's docs.

No longer works with TypeScript 2.4

TypeScript recently changed how decorators work (it's actually simpler), but autobind-decorator no longer works.

Here's an implementation that works under 2.4 (written in TypeScript) (only works for methods since that's all I use):

export default function autobind(target: any, key: string, descriptor: any): any {
  let fn = descriptor.value;
  let definingProperty = false;
  return {
    configurable: true,
    get() {
      if (definingProperty || this === target.prototype || this.hasOwnProperty(key)
        || typeof fn !== 'function') {
        return fn;
      }
      const boundFn = fn.bind(this);
      definingProperty = true;
      Object.defineProperty(this, key, {
        configurable: true,
        get() {
          return boundFn;
        },
        set(value) {
          fn = value;
          delete this[key];
        },
      });
      definingProperty = false;
      return boundFn;
    },
    set(value: any) {
      fn = value;
    },
  };
}

Can't minify version 2.2.0

When I deploy my project I get:

Failed to minify the code from this file:
./node_modules/autobind-decorator/src/index.js:7

New issue for version 2.2.0.

Break sinon.spy(Component.prototype, 'method')

When using '@autoBind' on a component, then in tests sinon.spy(Component.prototype, 'someMethod') will yield an error:

TypeError: Attempted to wrap undefined property undefined as function
...

Sorry, currently I can't provide more detail, but I'm sure @autobind is the only variable between success/error in tests.

@autobind race-condition

Hi, I have a complex react-native app, where I get a race-condition sometimes when having multiple setState firing. In the error-case this appears not to be set, when the method is called.

I'm sorry to not be able to provide a test-case, but the issue disappears when I replace the @autoBind method with an es7 class property.

What I'd like to know is how this is possible. Is it possible that react calls a method on the class before @autoBind had the chance to adjust this?

Async support

Is async supported?

export default class Component extends React.Component {
	constructor(props) {
		super(props);
	}

    componentWillMount() {
        const { dispatch } = this.props;
        dispatch(init());
    }

    @autobind
    async compute () {
        const { dispatch } = this.props;
        let result = await dispatch(compute());
        console.log('computed:', result);
    }

    render() {
        const props = this.props;

        return (
            <div>
                <p>{props.fetching}</p>
                <p>{props.success}</p>
                <p>{props.failure}</p>
                <button type="button" onClick={this.compute} value={'Compute'} />
            </div>
        );
    }
}

Autobind doesn't bind functions to class prototype

So while writing tests, I have a component:

import React, { Component } from 'react';
import autobind from 'autobind-decorator';

@autobind
export default class NavBar extends Component {
  handleClick() {
    console.log("Clicked!");
  }

  render() {
    return (
      <div>
        <a role="button" onClick={this.handleClick}>
          Click this
        </a>
      </div>
    );
  }
}

And then inside the my tests for Enzyme, using Karma/Jasmine:

describe("COMPONENTS: <NavBar />", function() {
  it("simulates click event for acceptingChats false", function() {
    const handleClick = spyOn(NavBar.prototype, 'handleClick');
    const rendered = shallow(<NavBar {...props} />);
    rendered.find('a').simulate('click');
    expect(handleClick).toHaveBeenCalled();
  });
});

However, this doesn't work because autobind doesn't actually put them inside the prototype of the class. Was wondering if there was a possibility for it to be bound to the prototype when done this way or just to each function in particular.

Assigning key to a function using Object.defineProperty fails in IE11

Line 87 in index.js causes stack overflow in IE11, my take is that placing Object.defineProperty inside a get method causes it to be re-triggered by Object.defineProperty. Easy fix is not to use Object.defineProperty and instead use old fashioned key assignment a.k.a

this[key] = boundFn;

It looks like you're making it writable and configurable, so Object.defineProperty might be an overkill for this.

Rationalle for autobind decorator comparing to = () => syntax sugar

In time of introducing React 0.13 Facebook devs ran into same problem and proposed to autobind method syntactically as:

class Counter extends React.Component {
  tick = () => {
    ...
  }
  ...
}

This syntax already work when using Babel with stage=0 setting, so I want to understand what problems solved @autobind decorator and is it ok, when one goal reached by two completely different ways?

More about autobinding from React v0.13.0 Beta 1 blog post

Does not work on windows

Hi,

It seems that this does not work on a windows box. it fails on mkdir -p. When I run it from git bash then I have the same error. It does also create a filder called -p on build.

Jan

Usage outside classes

Sometimes I'll find myself wiring up a singleton for use throughout my application. Said singleton is really just an object with a set of exposed methods.

Some of these methods modify the exposed singleton through Object.assign. Now, I don't want to pollute my codebase with references to this, as such autobind comes to mind as I use it throughout my classes.

export default function Lab () {
  function meth () {
    const recipe = { 'acetone', 'lithium', 'sulfuric acid' };
    Object.assign(this, { recipe });
  }

  Object.assign(this, { meth });
}

Now, the exposed method can be prebound with .bind just fine;

export default function Lab () {
  Object.assign(this, {
    meth: meth.bind(this)
  });
}

But if I wan't to call it before exposure (insert w/e reason);

export default function Lab () {
  function meth () { /**/ }

  meth.call(this);

  Object.assign(this, {
    meth: meth.bind(this)
  });
}

I'd much rather have:

import autobind from 'autobind-decorator';

export default function Lab () {
  @autobind
  function meth () { /**/ }

  Object.assign(this, { meth });
}

I have no clue as to how hard this would be to implement, nor if it would ever work. Thoughts?

autobind doesn't work with typescript

  @autobind
  handleReset () {
    this.setState({
      imgSrc: undefined
    });
  }

That code will throws an error in the browser console.
This is the error message.

App.tsx:127 Uncaught TypeError: Cannot read property 'setState' of null
at App.handleReset (http://localhost:3000/static/js/bundle.js:33337:14)
at Object.ReactErrorUtils.invokeGuardedCallback (http://localhost:3000/static/js/bundle.js:17522:17)
at executeDispatch (http://localhost:3000/static/js/bundle.js:17305:22)
at Object.executeDispatchesInOrder (http://localhost:3000/static/js/bundle.js:17328:6)
at executeDispatchesAndRelease (http://localhost:3000/static/js/bundle.js:16716:23)
at executeDispatchesAndReleaseTopLevel (http://localhost:3000/static/js/bundle.js:16727:11)
at Array.forEach (native)
at forEachAccumulated (http://localhost:3000/static/js/bundle.js:17625:10)
at Object.processEventQueue (http://localhost:3000/static/js/bundle.js:16930:8)
at runEventQueueInBatch (http://localhost:3000/static/js/bundle.js:24552:19)

Here's my tsconfig.json configuration

{
  "compilerOptions": {
    "outDir": "build/dist",
    "module": "commonjs",
    "target": "es5",
    "lib": ["es6", "dom"],
    "sourceMap": true,
    "allowJs": true,
    "jsx": "react",
    "moduleResolution": "node",
    "rootDir": "src",
    "forceConsistentCasingInFileNames": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    // "noImplicitAny": true,
    "strictNullChecks": true,
    "suppressImplicitAnyIndexErrors": true,
    "noUnusedLocals": true,
    "experimentalDecorators": true
  },
  "exclude": [
    "node_modules",
    "build",
    "scripts",
    "acceptance-tests",
    "webpack",
    "jest",
    "src/setupTests.ts"
  ],
  "types": [
    "typePatches"
  ]
}

autobind make react-hot fails

error stack

Uncaught TypeError: Cannot set property handleFormSubmit of #<ActivityForm> which has only a getter
    at patchProperty (webpack:///./~/.0.4.7@react-hot-api/modules/makeAssimilatePrototype.js?:21:16)
    at eval (webpack:///./~/.0.4.7@react-hot-api/modules/makeAssimilatePrototype.js?:52:9)
    at Array.forEach (native)
    at eval (webpack:///./~/.0.4.7@react-hot-api/modules/makeAssimilatePrototype.js?:51:51)
    at Array.forEach (native)
    at reconcileWithStoredPrototypes (webpack:///./~/.0.4.7@react-hot-api/modules/makeAssimilatePrototype.js?:50:21)
    at assimilatePrototype (webpack:///./~/.0.4.7@react-hot-api/modules/makeAssimilatePrototype.js?:63:5)
    at patchReactClass (webpack:///./~/.0.4.7@react-hot-api/modules/makePatchReactClass.js?:40:5)
    at Object.makeHot (webpack:///./~/.0.4.7@react-hot-api/modules/makeMakeHot.js?:33:12)
    at makeExportsHot (webpack:///./~/.1.3.1@react-hot-loader/makeExportsHot.js?:44:26)

1.3.1 build appears to be broken

I can't find anything in the source code that would be generating the code that is breaking the current package build so maybe the code was manually modified before it was pushed to npm?

When you npm install autobind-decorator, line 39 of node_modules/autobind-decorator/lib/index.js contains this line: console.log(Object.getOwnPropertyNames(), Object.getOwnProperySymbols()); which blows up because both of those Object.* calls expect an argument.

Not seeing this console.log in the source code on GitHub but it's coming down in the package source when you npm install.

Here's the full boundClass method in the npm install'ed code:

/**
 * Use boundMethod to bind all methods on the target.prototype
 */
function boundClass(target) {
  // (Using reflect to get all keys including symbols)
  console.log(Object.getOwnPropertyNames(), Object.getOwnProperySymbols());
  Reflect.ownKeys(target.prototype).forEach(function (key) {
    // Ignore special case target method
    if (key === 'constructor') {
      return;
    }

    var descriptor = Object.getOwnPropertyDescriptor(target.prototype, key);

    // Only methods need binding
    if (typeof descriptor.value === 'function') {
      Object.defineProperty(target.prototype, key, boundMethod(target, key, descriptor));
    }
  });
  return target;
}

Missing License

The package.json file says MIT license, but no licence is included. The actual source says the copyright is personal without granting any rights to other users. It would be great if this could be straightened out.

/**
 * @copyright 2015, Andrey Popp <8mayday@gmail.com>
 *

Parsing error: Unexpected character '@'

Hi, I'm using webpack, babel-loader and the following webpack.config.js

...
module: {
        preLoaders: [{
            test: /\.js$/,
            loader: "eslint?fix=true&configFile=.eslintrc.webpack.json",
            exclude: /(node_modules|bower_components)/
        }],
        loaders: [{
            test: /\.jsx?$/,
            exclude: /(node_modules|bower_components)/,
            loader: 'babel', // 'babel-loader' is also a legal name to reference
            query: {
                presets: [
                    'es2015',
                    'react',
                    'stage-0'
                ],
                plugins: [
                    'lodash',
                    'transform-decorators-legacy',
                    'export-default-module-exports',
                    ['transform-es2015-modules-commonjs-simple', {
                        noMangle: true
                    }]
                ],
                compact: false,
                sourceMaps: true
            }
        },
...

I have no .babelrc.

However, when I run webpack, it complains about the @ syntax.

Reproducable with the following file:


// Vendors
const React = require('react');
import autobind from 'autobind-decorator'

@autobind
class AccountSettingsInputContainer extends React.Component {

}
export default AccountSettingsInputContainer;

Package.json includes:

"autobind-decorator": "^1.3.3",
    "autoprefixer": "^6.3.6",
    "babel-cli": "^6.10.1",
    "babel-core": "^6.9.0",
    "babel-loader": "^6.2.4",
    "babel-plugin-autobind-class-methods": "^1.0.4",
    "babel-plugin-export-default-module-exports": "^0.0.4",
    "babel-plugin-lodash": "^3.2.0",
    "babel-plugin-transform-decorators-legacy": "^1.3.4",
    "babel-plugin-transform-es2015-modules-commonjs-simple": "^6.7.4",
    "babel-polyfill": "^6.9.0",
    "babel-preset-es2015": "^6.9.0",
    "babel-preset-react": "^6.5.0",
    "babel-preset-stage-0": "^6.5.0",

I thought stage-0 was supposed to give me the ability to write:

class ClassName extends Other {
    boundMethod = () => {
        // ...
    }
}

But I can't do that, either. Wondering if it just isn't something in my config. Anyone notice anything odd?

Default export gets overwritten in generated code (breaks usage with TypeScript)

In the current build of the library, at the top of the file we have

exports['default'] = autobind;

but then at the end we have

module.exports = exports['default'];

The latter wipes out the former, eliminating the default export. It probably usually goes unnoticed because with Babel, a default import falls back to importing the entire module, so

import autobind from 'autobind-decorator'

becomes the same as

import * as autobind from 'autobind-decorator'

Unfortunately TypeScript is less forgiving, requiring you to get the default import vs import * right, and as a result this package doesn't work out of the box with typescript. It also seems impossible to assign a functional type to an import *, which is what we need to do in this case. So, long story short, I think the solution is to upgrade Babel so the default export works as expected.

package.json module field points to uncompiled source

v2.3.1 introduced the env-preset for babel breaking our builds because we do not include it. I found the issue #75 but was unable to comment so I'm creating a new one. I have read over https://babeljs.io/blog/2018/06/26/on-consuming-and-publishing-es2015+-packages which you are giving as the reason for why it points to uncompiled source. However, in the article it states

"We should continue to publish ES5/CJS under main for backwards compatibility with current tooling but also publish a version compiled down to latest syntax (no experimental proposals) under a new key we can standardize on like main-es. (I don't believe module should be that key since it was intended only for JS Modules)"

Which indicates that module is not the correct place to do this due to backwards compatibility. We use modules before main in our webpack to get the benefits of import statement treeshaking but still require the rest to be es15 compatible.

Is it possible to have the module point to an es5 compatible version with imports instead of requires as is intended for the module field?

Not Compatible Under IE 11

Because of the Template Spring `@boundMethod decorator can only be applied to methods not: ${typeof r}`
Hope for update.

module tag in package.json is pointing to src not lib

On this commit: https://github.com/andreypopp/autobind-decorator/commit/9edeabf87c29d66e6d3d7ebffd715c05be119470#diff-b9cfc7f2cdf78a7f4b91a753d10865a2 the module tag was added pointing to src/index.js.

Usually, people assume the code in modules is already transpiled to be at least IE11 compliant, but src/index isn't. Only lib/index.js is.

Also, that tag was added without any change in the module version.

Thanks

`this.props` evaluates to `undefined` when using @autobind

I have a weird behaviour with about 300 Components rendered in a ListView where for some of them, I get an error because for some reason this.props is undefined in an auto binded method. If I use the usual syntax to bind my method in the constructor, the error goes away. I do not get the error in every Component, only some of them, in particular those at the end of the listView. All components are rendered the same way. I have the same behaviour when using my own autobind method inspired from react-autobind.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.