aethos
aethos

Reputation: 45

Unable to bind 'this' when passing a function as props in React

My Letter component needs to be reused multiple times, with minor changes in what happens in its constructor. My idea was to pass a function as props to Letter, so I can call it in the constructor. However, I'm having a problem with this.

In the code below, when I invoke this.props.setMessage.call(this), this refers to Container and not Letter, and so the state of Letter remains unchanged. I can see that this refers to Container by inserting console.log(this) in the body of the setMessage function in Container.

How do I bind this such that the state of Letter will change when I run this code?

Source:

export class Container extends React.component {

    ...

    setMessage = () => {
        this.state.message = 'hello';
    }

    render () {
        return (
            <div>
                <Letter setMessage={this.setMessage} />
            </div>  
    }
}

export class Letter extends React.component {
    constructor (props) {
        super(props);
        this.props.setMessage.call(this); // doesn't work, 'this' should refer to Letter but refers to Container
    }

    render () {
        ...
    }
}

Upvotes: 1

Views: 567

Answers (2)

Mike Cluck
Mike Cluck

Reputation: 32511

That's because you're using an arrow function.

Arrow functions immediately and permanently bind to the lexical context. Or to put it simply in this case, this will always be a Container.

To fix this, simply define it as a normal function.

export class Container extends React.Component {
  ...
  setMessage() {
    // As Mayank Shukla pointed out, this is how you should change state
    this.setState({ message: 'hello' });
  }
  ...
}

In general, setState is the correct way of updating state. However, if you try to use setState immediately in the constructor then you will get an error.

So just know that if you're setting that state anywhere but the constructor, setState is the right way of doing so. In the constructor, you should assign the initial state directly, as you were doing before.

Upvotes: 6

Howon Byun
Howon Byun

Reputation: 1

Try

export class Container extends React.component {

    ...

    setMessage() {
        this.setState({
            message: 'hello'
        });
    }

    render () {
        return (
            <div>
                <Letter setMessage={this.setMessage} />
            </div>  
    }
}

export class Letter extends React.component {
    constructor (props) {
        super(props);
    }

    setMessage() {
        this.props.setMessage();
    }

    render () {
        ...
    }
}

"this" binds to the immediate scope when used in arrow functions Also, prefer to use this.setState over this.state. = ...

Upvotes: 0

Related Questions