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

Autobind decorator #125

Closed
slonoed opened this issue May 6, 2015 · 26 comments
Closed

Autobind decorator #125

slonoed opened this issue May 6, 2015 · 26 comments
Milestone

Comments

@slonoed
Copy link

slonoed commented May 6, 2015

I want to use autobind decorator for components.

This https://github.com/andreypopp/autobind-decorator/blob/master/src/index.js work fine with React (method binding).
But have issue with RHL. Decorator bind method to instance. When new RHL add new prototype to prototype chain nothing happened, because method already in instance and new method in proto not visible when getter called.

I use ugly hack and check in getter if (this.props) to determine is this – instance.

It is not an issue with RHL or decorator itself. I try to find nice solution.

@chrisvariety
Copy link

The exact message I get when trying this is Cannot set property fooBarMethod of #<Component> which has only a getter

@gaearon
Copy link
Owner

gaearon commented May 6, 2015

You can try finding a fix by adding a failing test to react-hotify. Then, if you find a solution, I can backport it to RHL.

@slonoed
Copy link
Author

slonoed commented May 6, 2015

@gaearon should I add test for work with autobind-decorator?

@gaearon
Copy link
Owner

gaearon commented May 6, 2015

@slonoed Yes, if you also find a fix.

@gaearon
Copy link
Owner

gaearon commented May 6, 2015

@slonoed It's also worth checking if this change helps (or makes it worse, I dunno)

@vasa-chi
Copy link

Dunno if much help, but here is a test for react-hotify for this problem (using core-decorators):

import React, { Component } from 'react';
import createShallowRenderer from './helpers/createShallowRenderer';
import expect from 'expect.js';
import makeHotify from '../src/makeHotify';

import { autobind } from 'core-decorators';

class Bar {

  @autobind
  getProps() {
    return typeof this.props;
  }

  render() {
    return <div>{this.getProps()}</div>;
  }
}

describe('autobind usage', function() {

  let renderer;
  let hotify;

  beforeEach(() => {
    renderer = createShallowRenderer();
    hotify = makeHotify();
  });

  it('should bind methods of hotified components', function() {
    const HotBar = hotify(Bar);
    const barInstance = renderer.render(<HotBar />);
    expect(renderer.getRenderOutput().props.children).to.equal('object');
  });
})

@gaearon
Copy link
Owner

gaearon commented Jun 29, 2015

Thanks! I'll definitely get back to this after my React Europe talk.

@cpsubrian
Copy link

Happy to help test possible fixes. Also wondering if this is a general problem interoperating with decorators in general, and if that should be the focus rather than @autobind in particular?

@slonoed
Copy link
Author

slonoed commented Jul 16, 2015

For me this issue not actual now. I switch to arrow functions in class definition.

class Cmp extends Component {
  myBindedFunc = () => {
     this.callSmth();
  }
}

Can I close this issue?

@cpsubrian
Copy link

I've done the same, but consider it a temporary fix. I prefer the decorator approach (because I bind at the class level, not the method level). I will chime back in here if my other decorators have issues (I haven't gotten that far yet, this is my first hot-reload project).

@lelandrichardson
Copy link

@slonoed this arrow function approach works? Will it always? I would have thought that in that scenario the arrow function would have made this bound to the this in the context outside of your class definition. Is this just a convenient bug or is this actually the spec'd behavior of arrow functions inside class definitions?

@opatut
Copy link

opatut commented Jul 17, 2015

I am having issues with the arrow function, actually. The hot reloading does not apply when using render () => { ... } instead of render () {}. Hence, no 'autobinding' :( Any ideas on how to fix this?

@slonoed
Copy link
Author

slonoed commented Jul 18, 2015

@lancefisher I think there is no spec for arrow functions in class.
This is babel stuff https://gist.github.com/jeffmo/054df782c05639da2adb
Babel put this functions assignments in constructor. And bound to this in constructor (i.e. instance of class).

@opatut why you want bind render? It is always called by library code, isnt it?

@opatut
Copy link

opatut commented Jul 19, 2015

It's not only about binding render, when I add other methods, such as event callbacks, I cannot change logic inside them with hot reloading, since they are not proxied.

However, I find that binding the methods with .bind() or .call() upon usage works great, especially after I discovered the new (stage 0) function bind syntax. Example:

class MyComponent extends React.Component {
    onClick () {
        console.log(this); // `this` is bound
    }

    render () {
        return <button onClick={::this.onClick}>Click me</button>;
    }
}

@gaearon
Copy link
Owner

gaearon commented Jul 19, 2015

Properties are very tricky to hot reload because they're really being defined inside the constructor.
We'll see what we can do with a Babel plugin later, but for now, the workaround from @opatut is the best we got.

@gaearon
Copy link
Owner

gaearon commented Aug 22, 2015

We're supporting autobind decorator in the next beta release coming soon.
Stay tuned.

@gaearon gaearon closed this as completed Aug 22, 2015
@gaearon gaearon added this to the v2.0 milestone Aug 22, 2015
@gaearon
Copy link
Owner

gaearon commented Aug 23, 2015

Can I ask you to check whether [email protected] solves this issue?
Please provide any feedback on it in this PR: #182

@steadicat
Copy link

2.0.0-alpha fixes hot reload of fat arrow methods for me. Thank you!

@gaearon
Copy link
Owner

gaearon commented Aug 24, 2015

@steadicat Hmm, I'm pretty sure fat arrow methods still shouldn't get hot reloaded. Can you check again, or share a reproducible case?

@gaearon
Copy link
Owner

gaearon commented Aug 24, 2015

(That said @autobind decorator should work now. Just not the fat arrows.)

@steadicat
Copy link

@gaearon You're right, it turns out I have a decorator on my components that somehow works around this issue. Here’s a reduced test case (code below):

import React from 'react';

function wrap(Component) {
  return class Wrapper {
    render() {
      return <Component {...this.props} />;
    }
  }
}

@wrap
export default class Test {

  render() {
    return this.renderInner();
  }

  renderInner = () => {
    return <h1>Version 4</h1>;
  }

}

@gaearon
Copy link
Owner

gaearon commented Aug 24, 2015

Oh. It works because it generates a new class on every invocation. The downside is Test state will be reset on every hot reload.

@yangmillstheory
Copy link

yangmillstheory commented Jun 4, 2016

I don't think this is fully working with [email protected] yet.

Small test commit here.

jun-04-2016 12-03-46

In the demo above there's 3 steps:

  1. without @autobind on onClick, click the button. This doesn't have the right context - this is null.
  2. add @autobind to onClick - this is recognized by RHL and correct logs the right state in the console (this is bound to the Counter instance).
  3. remove @autobind, this is still bound to the Counter instance (you would expect it not to be).

RHL doesn't remove the binding when using fat arrows to declare the method either.

Hope that's helpful, would really love to use this with RHL!

@geyang
Copy link

geyang commented Jun 29, 2016

I'm having trouble too, no production ready example yet.

It might make sense to reopen this issue.

@gaearon
Copy link
Owner

gaearon commented Jun 30, 2016

Add/removing @autobind is not going to be hot reloaded correctly—it doesn’t seem to be very useful to support this corner case. But tweaking @autobound methods should work. If not, please file a new issue with a reproducing project.

@geyang
Copy link

geyang commented Jun 30, 2016

got it! I moved to the beta and everything worked after I updated to node 6.x, so it is all good!
Thanks for the response.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants