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

ES6-style classes & JSX binding doesn't work as expected. #1436

Closed
jaygarcia opened this issue May 28, 2015 · 9 comments
Closed

ES6-style classes & JSX binding doesn't work as expected. #1436

jaygarcia opened this issue May 28, 2015 · 9 comments
Labels
Resolution: Locked This issue was locked by the bot.

Comments

@jaygarcia
Copy link
Contributor

This is a fun one :)

I'm in need of class hierarchy and thought that using ES6-style classes would be the best way to achieve this result using basic ES6 OOP patterns. I've got things mostly working, but found that the JSX method binding doesn't actually act as (how I) expected and need someone to help me understand why.

The Breakdown

My class's render function returns a block of components that bind this.onButtonPress. Nothing out of the ordinary.

        return (
             ...
                <View style={styles.controlsContainer}>
                    <MusicControlButton onPress={this.onButtonPress} btnChar={"dislike"} btnStyle={"dislikeButton"} isLikeBtn={true}/>
                    <MusicControlButton onPress={this.onButtonPress} btnChar={"prev"} btnStyle={"prevButton"}/>
                    <MusicControlButton onPress={this.onButtonPress} btnChar={centerBtnChar} btnStyle={centerBtnStyle}/>
                    <MusicControlButton onPress={this.onButtonPress} btnChar={"next"} btnStyle={"nextButton"}/>
                    <MusicControlButton onPress={this.onButtonPress} btnChar={"like"} btnStyle={"likeButton"} isLikeBtn={true}/>
                </View>                     
            </View>
        );

onButtonPress references a map to do method calls. (way cleaner than doing if/else and/or switch/case).

    onButtonPress(buttonType) {
        var methodName = this.methodMap[buttonType];
        this[methodName] && this[methodName]();
    }

The method map is a plain'ol JavaScript object:

(JSX Version)

    methodMap : {
        'prev'  : 'previousTrack',
        'next'  : 'nextTrack',
        'play'  : 'playTrack',
        'pause' : 'pauseTrack'
    },

    onButtonPress : function(buttonType) {
        var methodName = this.methodMap[buttonType];
        this[methodName] && this[methodName]();
    },

-----------
(ES6 version)
AbstractPlayer.prototype.methodMap = {
    prev     : 'previousTrack',
    next     : 'nextTrack',
    play     : 'playTrack',
    pause    : 'pauseTrack',
    like     : 'like',
    dislike  : 'dislike'
};

So here's the rub.

In the JSX Version, the binding works as expected this is a reference to the instance of the class.
es52

In the ES6 version, the binding (this) points to a plain javascript object.
es5

Here's the original class (Using React.createClass()):
https://gist.github.com/jaygarcia/e68682c284ded8e6cea0#file-gistfile1-js-L123

Here's the ES6 version.
https://gist.github.com/jaygarcia/fea5905ee361864aa5d9#file-gistfile1-js-L99

What did I do wrong? I tried to follow the Extensibility guide, but it didn't help much:
https://github.com/facebook/react-native#extensibility

/cc @grgur

@ide
Copy link
Contributor

ide commented May 28, 2015

ES6 React classes don't autobind their methods (documentation here) so you have three options:

  1. Bind the methods yourself in the constructor() function
  2. Use the property syntax like onButtonPress = buttonType => { ... };. This might require adjusting the options that the packager passes to Babel.
  3. Use decorators like @autobind onButtonPress() { ... }. Again this might require a small patch to the packager.

@brentvatne
Copy link
Collaborator

@ide nailed it, not much else to say here 😄

Awesome level of detail in your report @jaygarcia 🚀

@jaygarcia
Copy link
Contributor Author

@ide , thanks for the documentation. I somehow missed that.

@brentvatne , thanks for closing this :)

@jaygarcia
Copy link
Contributor Author

So far, this is the only thing that work (Lexical scoping).

So, to currently use ES6-style classes with RN 0.4.4, I have to muddy up my code with adhoc binding.

I don't see the value at the current time :(

     var buttonPress = (x) => {
            this.onButtonPress(x)
        }

        return (
               .....

                <View style={styles.controlsContainer}>
                    ....
                    <MusicControlButton onPress={buttonPress} btnChar={centerBtnChar} btnStyle={centerBtnStyle}/>
                    ....
                </View>                     
            </View>
        );

@ruswerner
Copy link

I know it's been a while since this thread was alive, but I've had some luck by adding es7.functionBind to the transformer.js whitelist in the packager. Then you can do something like:

<MusicControlbutton onPress={::this.onButtonPress} />

It seems to work just fine for my needs, although my IDE (WebStorm 11) doesn't support the syntax yet...

@ghost
Copy link

ghost commented Jun 26, 2016

@ruswerner thanks, this is what I was looking for.
Is there a reason, why es7.functionBind is not included by default?

@ruswerner
Copy link

ruswerner commented Jun 26, 2016

@dropfen I'm not sure why, but it's probably because it's not finalised enough for them. Here is my rn-babelrc.json (search your project for it):

{
  "presets": [
    "react-native"
  ],
  "plugins": [
    "transform-decorators-legacy",
    "transform-export-extensions",
    "transform-function-bind"
  ]
}

You'll need to install those packages and restart the packager before it will all work. Hope that helps!

I'm on RN 0.26, which is quite different than when this thread was started.

@ghost
Copy link

ghost commented Jun 26, 2016

Hey @ruswerner! Thank you very much for this.

After some research I found that there is no need to modify the rn-babelrc.json file in node_modules / react-native / packager.

just create a .babelrc file in your project root with that content and it just works.

Hope that helps, too!

@ruswerner
Copy link

Yep. Much better. Thanks!

On Mon, Jun 27, 2016 at 10:05 AM, webdeb notifications@github.com wrote:

Hey @ruswerner https://github.com/ruswerner! Thank you very much for
this.

After some research I found that there is no need to modify the
rn-babelrc.json file in node_modules / react-native / packager.

just create a .babelrc file in your project root with that content and it
just works.

Hope that helps, too!


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#1436 (comment),
or mute the thread
https://github.com/notifications/unsubscribe/AA4YVMCbydF6pIT7TXWwFgRP3esDir-Rks5qPvewgaJpZM4Et29W
.

@facebook facebook locked as resolved and limited conversation to collaborators May 29, 2018
@react-native-bot react-native-bot added the Resolution: Locked This issue was locked by the bot. label Jul 22, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Resolution: Locked This issue was locked by the bot.
Projects
None yet
Development

No branches or pull requests

5 participants