32

I see a number of questions on here relating to this same issue, but it seems none match the issue I'm having, and are a bit more complex.

I am in the process of learning ReactJS and React Native. I'm in the midst of reading and following the code examples from "Learning React Native" book here: https://github.com/bonniee/learning-react-native

For some reason, calling this.setState in the code below when the handleTextChange function is called, causes the "this.SetState is not a function." error. My question is why? Unlike other questions about this same issue, I don't believe my call to this.stateState is buried in a callback function or if statement. Why is it undefined?

Here is my code:

class WeatherProject extends Component {
  constructor(props) {
    super(props);
    this.state = {
      zip: "",
      forecast: null
    };
  }

  _handleTextChange(event) {
    this.setState({zip: event.nativeEvent.text});
  }

    render() {
    return (
      <View style={styles.container}>
      <Text style={styles.welcome}>
      You input {this.state.zip}.
      </Text>
      <TextInput
      style={styles.input}
      onSubmitEditing={this._handleTextChange}/>
      </View>
    );
  }
}
Steed-Asprey
  • 2,001
  • 7
  • 24
  • 30
  • 14
    `onSubmitEditing={ this._handleTextChange.bind(this) }/>`, or `onSubmitEditing={() => this._handleTextChange() }/>` – Oleksandr T. Jul 18 '16 at 15:45
  • @AlexanderT. That worked great, thanks. So, why do I need to bind this to that even handler? – Steed-Asprey Jul 18 '16 at 15:47
  • You can see this article: https://medium.com/@razgoldin/using-react-es-6-syntax-fc05acf72810#.rab7bgxnw – Zargold Jul 18 '16 at 15:48
  • Possible duplicate of [Unable to access React instance (this) inside event handler](http://stackoverflow.com/questions/29577977/unable-to-access-react-instance-this-inside-event-handler) – Tom Fenech Jul 18 '16 at 15:58
  • I was running into the same issue as I believe we are reading the same book about react-native. I would like to add two coments: - For being able to use event.nativeEvent.text inside the function, the only option that worked for me was to use the .bind(this) way instead of this._handleTextChange(). Not 100% sure of the issue although it may be scope related. -This solution works way better as the text updates with each change: onChangeText={(text) => this.setState({zip: text}) – fray88 Sep 16 '16 at 12:51
  • also this article https://medium.com/@housecor/react-binding-patterns-5-approaches-for-handling-this-92c651b5af56 – Varun Nath Apr 27 '17 at 09:32

5 Answers5

51

Do not use bind inside a render. bind is a rather expensive operation and should only happen once. you have two options:

either bind the function in the constructor:

this._handleTextChange = this._handleTextChange.bind(this);

or use arrow function:

onSubmitEditing={(e) => this._handleTextChange(e)} />

Edit

Apparently arrow functions inside render is also a bad practice (Thx to Adam Terlson in the comments and answer below). You can read eslint docs which states:

A bind call or arrow function in a JSX prop will create a brand new function on every single render. This is bad for performance, as it will result in the garbage collector being invoked way more than is necessary.

Using arrow functions is obviously not as bad as using bind, but nevertheless should be avoided.

atlanteh
  • 5,615
  • 2
  • 33
  • 54
  • 1
    or the ES7 proposed feature, `::this._handleTextChange` – ZekeDroid Jul 18 '16 at 17:21
  • The performance impact of causing an unnecessary re-render on the child component by using an arrow in your render function is probably an order of magnitude worse than the performance of the `bind` function itself. – Adam Terlson Jul 18 '16 at 19:55
  • I've never heard of such a thing. Every tutorial out there is encouraging to use arrow functions instead of bind. can you please provide a source for what you are saying? – atlanteh Jul 18 '16 at 20:08
  • My comment wasn't in support of bind. It wasn't in support of arrows. It was making the point that doing either will cause a component prop change on whatever component receives the new function you just created and this may have huge performance implications. – Adam Terlson Dec 09 '16 at 07:47
  • @atlanteh when we use arrow function _handleTextChange(event) has to be changed to _handleTextChange = (event) => isn't it? – Hemadri Dasari Sep 05 '18 at 11:01
  • Yes. This is a class property so it's created once so it'll work as well. Just note that it's es7, so you'll need babel to support all browsers – atlanteh Sep 05 '18 at 13:48
18

Regarding arrow function you also need to change _handleTextChange(event) function. Other answers didn't talk about how to change normal function to arrow function.

You need to change handler function, from:

_handleTextChange(event) {
    this.setState({zip: event.nativeEvent.text});
  }

To:

_handleTextChange = event => {
   this.setState({zip: event.nativeEvent.text});
 }
halfer
  • 19,824
  • 17
  • 99
  • 186
Hemadri Dasari
  • 32,666
  • 37
  • 119
  • 162
  • Thank you, this worked for me. What I don't understand is why the `this` context changes in a local method, shouldn't that be the primary place to _maintain_ it context? – J-Cake Jan 14 '19 at 05:01
8

The issue is context binding, as identified in the other comments and answers here.

However, the performance of bind itself is a non-issue. The way-more-relevant issue is that using bind or arrows in your render methods creates a new function on each render, resulting in a change of props for the child that receives them, forcing a re-render.

You have two viable options:

class WeatherProject extends Component {
  constructor(props) {
    super(props);

    this._handleTextChange = this._handleTextChange.bind(this);
  }
  // ...
}

Or you can use the class property notation and assign arrow functions if you're using the babel plugin for it.

class WeatherProject extends Component {
  constructor(props) {
    super(props);
    // ...
  }

  handleTextChange = (event) => {
    this.setState({zip: event.nativeEvent.text});
  }
  // ...
}

I strongly recommend you use the eslint package with the react recommended rules enabled. It will catch errors like using bind/arrows in your render, as well as tell you that underscore prefixed functions are ugly and totally not necessary in React. :)

Adam Terlson
  • 12,610
  • 4
  • 42
  • 63
  • Actually according to eslint [docs](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md), the only problem is creating brand new function in every call, which is bad for performance, but it doesn't cause a child re-render – atlanteh Dec 07 '16 at 21:26
  • @atlanteh Yeah that's true but my broader point is still valid. Here's but one of many articles referencing the prop change issue. https://medium.com/@esamatti/react-js-pure-render-performance-anti-pattern-fb88c101332f?source=linkShare-583b3a685d22-1481269421 – Adam Terlson Dec 09 '16 at 07:45
1

this.setState is not a function--- Solved using this

 let that = this;

 that.setState({membersArray:response.data})
Keshav Gera
  • 10,807
  • 1
  • 75
  • 53
1

I had the same problem when trying to set the state. when I bind the function inside the constructor, the issue gets solved. check with below binding

 constructor(props) {
    super(props);
    this.state = {
      zip: "",
      forecast: null
    };

   this._handleTextChange = this._handleTextChange.bind(this);

  }
Dasun_96
  • 173
  • 1
  • 7