'Why this.state is undefined in react native?

I am a complete newbie in react native, react.js, and javascript. I am Android developer so would like to give RN a try.

Basically, the difference is in onPress;

This code shows 'undefined' when toggle() runs:

class LoaderBtn extends Component {
    constructor(props) {
        super(props);
        this.state = { loading: false };
    }

    toggle() {
        console.log(this.state);
        // let state = this.state.loading;
        console.log("Clicked!")
        // this.setState({ loading: !state })
    }

    render() {
        return (
            <Button style={{ backgroundColor: '#468938' }} onPress={this.toggle}>
                <Text>{this.props.text}</Text>
            </Button>
        );
    }
}

but this code works:

class LoaderBtn extends Component {
    constructor(props) {
        super(props);
        this.state = { loading: false };
    }

    toggle() {
        console.log(this.state);
        // let state = this.state.loading;
        console.log("Clicked!")
        // this.setState({ loading: !state })
    }

    render() {
        return (
            <Button style={{ backgroundColor: '#468938' }} onPress={() => {this.toggle()}}>
                <Text>{this.props.text}</Text>
            </Button>
        );
    }
}

Can you explain me the difference, please?

In Java / Kotlin we have method references, basically it passes the function if signatures are the same, like onPress = () => {} and toggle = () => {}

But in JS it doesn't work :(



Solution 1:[1]

The issue is that in the first example toggle() is not bound to the correct this.

You can either bind it in the constructor:

constructor(props) {
    super(props);
    this.toggle = this.toggle.bind(this);
    ...
    

Or use an instance function (OK under some circumstances):

toggle = () => {
    ...
}

This approach requires build changes via stage-2 or transform-class-properties.

The caveat with instance property functions is that there's a function created per-component. This is okay if there aren't many of them on the page, but it's something to keep in mind. Some mocking libraries also don't deal with arrow functions particularly well (i.e., arrow functions aren't on the prototype, but on the instance).

This is basic JS; this article regarding React Binding Patterns may help.

Solution 2:[2]

I think what is happening is a matter of scope. When you use onPress={this.toggle} this is not what you are expecting in your toggle function. However, arrow functions exhibit different behavior and automatically bind to this. You can also use onPress={this.toggle.bind(this)}.

Further reading -

ES6 Arrow Functions

.bind()

Solution 3:[3]

What is happening in this first example is that you have lost scope of "this". Generally what I do is to define all my functions in the constructor like so:

constructor(props) {
    super(props);
    this.state = { loading: false };
    this.toggle = this.toggle.bind(this);
}

In the second example, you are using ES6 syntax which will automatically bind this (which is why this works).

Then inside of you onPress function, you need to call the function that you built. So it would look something like this,

onPress={this.toggle}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 user5305519
Solution 2
Solution 3 Dave Newton