0

I am trying to call a function (with values of title and category) when a button is clicked. The values are stored in the state of the component, and the values are correct when I console.log(this.state). However, when the function is called, the value of title remains but category becomes undefined.

It works when I put the function that I want to call into another function, I'm not sure why the original binding caused one of the values to become undefined. Was "this" lost, and where was it lost at?

I have tried understanding the explanations in React this.state is undefined? , https://javascript.info/bind , Binding this? Getting undefined? and Are 'Arrow Functions' and 'Functions' equivalent / exchangeable? but I don't think it explains the behaviour experienced in mine.

Was I using too much this/bind which caused the problem?

class CreateItem extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            inputTitle: '',
            selectedCategory: ''
        }
    }

    // works when I put the createItem(title, category) inside this function
    onClickAddItem() {
        this.props.createItem(this.state.inputTitle, this.state.selectedCategory);
    }

    render() {
        <div>
        // this doesn't work (edit: due to typo where it should be selectedCategory and not inputCategory) 
        <Button text={'Create'} onClick={this.props.createItem.bind(this, this.state.inputTitle, this.state.inputCategory)}/>

        // this works
        <Button text={'Create'} onClick={this.onClickAddItem.bind(this)}/>
        </div>
    }
}

// createPublicChat function is in another file

export function createItem(title, category) {
    console.log(title, category);
}

I expected the output of title = "BookA", category = "Fiction" to print "Book A Fiction" to console, but the actual output is "Book A undefined"

Edit: There was a typo which caused the undefined issue (as pointed out by @bkm412). Both works now, thank you!

Tofu Lee
  • 95
  • 1
  • 9

3 Answers3

1

I think there is typo

<Button text={'Create'} onClick={this.props.createItem.bind(this, this.state.inputTitle, this.state.inputCategory)}/>

this.state.inputCategory should be changed to this.state.selectedCategory

bkm412
  • 1,047
  • 5
  • 11
  • binding inside `render` method is not a good idea. https://www.freecodecamp.org/news/why-arrow-functions-and-bind-in-reacts-render-are-problematic-f1c08b060e36/ – Amanshu Kataria Jul 03 '19 at 09:21
1

I have noticed a few bad practices in this code. The better way of calling a function with some arguments is follows:

<Button onClick={() => this.props.createItem(this.state.title, this.state.selectedcategory)} />

Also to prevent the binding errors, one should bind all your functions at one place i.e. inside constructor.

constructor(props) {
        super(props);
        this.state = {
            inputTitle: '',
            selectedCategory: ''
        }
        this.createItem = this.createItem.bind(this);
    }

Also you do not need to bind the functions that are passed to the component as props.

Harshit Agarwal
  • 2,308
  • 2
  • 15
  • 23
1

Firstly never bind a function inside the render method because whenever a component is re-rendered, it'll do a new binding. If you want to bind a function the perfect place is inside the constructor method.

Alternatively you can use arrow functions to avoid binding issues.
e.g: onClickAddItem=()=>{}

Doing so you won't have to bind the class function to this explicitly because arrow function don't have this associated with it and refers to the enclosing lexical scope.

Read more about arrow function here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

Amanshu Kataria
  • 2,838
  • 7
  • 23
  • 38