Как получить доступ к `this` внутри renderRow ListView?

У меня возникли проблемы с запуском функции для события onPress внутри строки ListView. Я следую учебному руководству React Native, пытаясь продолжить оттуда. Кажется, он использует стиль синтаксиса ES6.

Это важная часть кода.

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */

import React, {  
  TouchableHighlight,
  AppRegistry,
  Component,
  Image,
  ListView,
  StyleSheet,
  Text,
  View,
  Alert,
} from 'react-native';

class AwesomeProject extends Component {
  constructor(props) {
    super(props);
    this.something = this.something.bind(this); // <-- Trying this, not even sure why
    this.state = {
      dataSource: new ListView.DataSource({
        rowHasChanged: (row1, row2) => row1 !== row2,
      }),
      loaded: false,
    };
  }

//
//Irrelevant code here. Fetching stuff and renderLoadingView
//

  something = function(){
    console.log('something');
    Alert.alert(
      'Alert Title',
      'alertMessage',
    );
  }


  render() {
    console.log('this', this); //this is an instance of AwesomeProject
    if (!this.state.loaded) {
      return this.renderLoadingView();
    }

    return (
      <ListView
        dataSource={this.state.dataSource}
        renderRow={this.renderMovie}
        style={styles.listView}
        />
    );
  }

  renderMovie(movie) {
    console.log('Not this', this); //this is not an instance of AwesomeProject
    return (
      <TouchableHighlight onPress={() => {console.log(this); this.something()}}>
     <View style={styles.container}>
        <Image
          source={{uri: movie.posters.thumbnail}}
          style={styles.thumbnail}                    
        />
        <View style={styles.rightContainer}>
          <Text style={styles.title}

          >{movie.title}</Text>
          <Text style={styles.year}>{movie.year}</Text>
        </View>
      </View>
       </TouchableHighlight>
    );
  }
}

//
//More irrelevant code here. Styles and the 
//

AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject);

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

Единственным способом, с помощью которого я должен был работать, было объявить внешнюю переменную с var something = function(){...} вне класса AwesomeProject.

Есть ли способ доступа к something при объявлении внутри AwesomeProject? Возможно, это плохая практика в соответствии с архитектурой потока или чем-то еще?

Ответ 1

Оказывается, я нашел решение сам, здесь: https://github.com/goatslacker/alt/issues/283#issuecomment-115391700

Проблема в том, что с синтаксисом ES6 у вас нет автоматической привязки this, поэтому вам нужно сделать это самостоятельно.

Я узнал немного больше, так как я опубликовал этот ответ. Настоящая проблема заключается в том, что this является "связанным" при выполнении функции, а не при ее объявлении. Он указывает на объект, который функция прикреплена к при вызове. Например:

obj.foo(); //inside foo, this points to obj
bar(); //inside bar, this points to the global object (or undefined in strict mode)

В моем примере функция this.renderMovie() передается как параметр. Так что внутри ListView это будет "локальная переменная". Он будет называться "отделенным" от любого объекта, как и в примере с bar(), поэтому this не будет тем, что я ожидаю.

Возможно принудительное связывание this.

Есть 3 возможных решения моей проблемы, и мне больше всего нравится 3-е:

Вариант 1: связать это вручную при вызове

В коде замените ссылку на {this.renderMovie} на {this.renderMovie.bind(this)}

  render() {
    console.log('this', this); //this is an instance of AwesomeProject
    if (!this.state.loaded) {
      return this.renderLoadingView();
    }

    return (
      <ListView
        dataSource={this.state.dataSource}
        renderRow={this.renderMovie.bind(this)} //<-- change here
        style={styles.listView}
        />
    );
  }

Вариант 2. Выполните привязку к конструктору

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

  constructor(props) {
    super(props);
    this.something = this.something.bind(this); // <-- Not this function
    this.renderMovie = this.renderMovie.bind(this); // <-- This function!!
    this.state = {
      dataSource: new ListView.DataSource({
        rowHasChanged: (row1, row2) => row1 !== row2,
      }),
      loaded: false,
    };
  }

Вариант 3: используйте синтаксис стрелок и позвольте им выполнять свою работу

Мне нравится этот самый лучший. Вам просто нужно объявить задействованную функцию, используя синтаксис стрелки, вот так:

  renderMovie(movie) {
    //code here
  }

становится таким:

  renderMovie = (movie) => {
    //code here
  }

Ответ 2

В дополнение к fooobar.com/info/557205/...

Вариант 4: использовать оператор привязки

<ListView
    dataSource={this.state.dataSource}
    renderRow={::this.renderMovie} //<-- cleaner, than bind
    style={styles.listView}
    />

Будьте осторожны, это просто предложение, а не стандарт!