React Native: this.setState не является функцией

Я вижу здесь несколько вопросов, относящихся к этой же проблеме, но, похоже, не соответствует той проблеме, которая у меня есть, и немного сложнее.

Я участвую в процессе обучения ReactJS и React Native. Я посреди чтения и следуя примерам кода из книги "Learning React Native": https://github.com/bonniee/learning-react-native

По какой-то причине вызов this.setState в коде ниже, когда вызывается функция handleTextChange, вызывает "this.SetState не является функцией". ошибка. Почему мой вопрос? В отличие от других вопросов по этой же проблеме, я не считаю, что мой вызов this.stateState похож на функцию обратного вызова или оператор if. Почему это undefined?

Вот мой код:

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>
    );
  }
}

Ответ 1

Не используйте привязку внутри рендера. bind - довольно дорогостоящая операция и должна произойти только один раз. у вас есть два варианта:

либо привязать функцию в конструкторе:

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

или используйте функцию стрелки:

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

Изменить

Очевидно, что функции стрелок внутри рендера также являются плохой практикой (спасибо to Adam Terlson в комментариях и ответах ниже). Вы можете прочитать eslint docs, который гласит:

Функция привязки или функция стрелки в JSX prop создаст совершенно новую функцию для каждого отдельного рендеринга. Это плохо для производительности, так как это приведет к тому, что сборщик мусора будет вызван больше, чем это необходимо.

Использование функций стрелок, очевидно, не так плохо, как использование bind, но, тем не менее, следует избегать.

Ответ 2

Проблема связана с контекстом, как указано в других комментариях и ответах здесь.

Однако производительность самого связывания не является проблемой. Еще более важная проблема заключается в том, что использование привязки или стрелок в методах рендеринга создает новую функцию для каждого рендеринга, что приводит к изменению реквизита для получающего их ребенка, заставляя повторную визуализацию.

У вас есть два жизнеспособных варианта:

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

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

Или вы можете использовать обозначение свойства класса и назначить функции стрелок, если вы используете для него плагин babel.

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

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

Я настоятельно рекомендую вам использовать пакет eslint с отредактировать рекомендуемые правила. Он будет ловить ошибки, например, используя bind/стрелки в вашем рендере, а также сказать, что подчеркивающие префиксные функции уродливы и абсолютно не нужны в React.:)

Ответ 3

Что касается функции стрелки, вам также необходимо изменить функцию _handleTextChange (событие). Другие ответы не говорили о том, как изменить нормальную функцию на функцию стрелки. Таким образом, предоставление в качестве ответа, который может помочь другим

Вам нужно изменить функцию обработчика

от

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

к

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

Ответ 4

Я не могу решить это. Я перепробовал все решения, которые я могу найти в Интернете. Вот мой код:

constructor() {
super();
this.state = {
  list: [],
  favourites: []
};

Это функции

  _favouriteIcon = (item) => {
let color = this.getState('favourites').indexOf(item.vehicleid)!== -1?'#ffb30c':'#cacaca';
return <TouchableOpacity onPress={() => this._toggleFavourite(item)}>
  <Icon
    name='star'
    color={'${color}'}/>
</TouchableOpacity>

};


_renderItem = (item, rowId, sectionId) => {
console.log(item);
let text = '';
let subText = '';

return <TouchableOpacity onPress={() => this.onPress(item)}>
  <ListItem
    leftIcon = {this._favouriteIcon(item).bind(this)}
    title={'${text}'}
    subtitle={'${subText}'}
    containerStyle={{borderBottomWidth: 0}}
  />
</TouchableOpacity>
};

render() {
return (
  <View style={styles.container}>
    <ExpandableList
      dataSource={this.state.list}
      headerKey="title"
      memberKey="member"
      renderRow={this._renderItem.bind(this)}
      renderSectionHeaderX={this._renderSection}
    />
    <Footer/>
  </View>
);
}