Как получить доступ к `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}
/>
Будьте осторожны, это просто предложение, а не стандарт!