Давайте создадим нативное приложение React для заполнения списка фильмов из API. Мы покажем пользователю изображение и название фильма. Обратите внимание, что я использовал семантику ES6, такую как стрелочные функции и классы в JS.
Давайте инициализируем проект, введя следующее в CLI.
react-native init MovieList
Теперь перейдите в каталог и откройте index.android.js.
Мы собираемся удалить весь код здесь. После удаления добавьте следующие операторы импорта
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
ListView,
Image
} from 'react-native';
Добавьте Appregistry в конце следующим образом
AppRegistry.registerComponent('MovieList', () => MovieList);
Теперь мы определяем после этого следующим образом
export default class MovieList extends Component {
constructor(){
super();
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows([])
};
}
render(){
return(
<ListView>
</ListView>
);
}
}
Мы создаем состояние dataSource, это состояние будет содержать все данные, которые требуются ListView.
Компонент ListView требует двух реквизитов: dataSource и renderRow. dataSource является источником информации для списка. renderRow берет один элемент из источника и возвращает отформатированный компонент для рендеринга.
Теперь нам нужно определить функцию, которая будет получать данные из API. Итак, внутри класса MovieList я определяю функцию
_refreshData = ()=> {
console.log('refresh data was called');
var endpoint = "https://sampleapi.com";
fetch(endpoint)
.then((response)=> response.json())
.then((responseJSON)=>{
this.setState({
dataSource: this.state.dataSource.cloneWithRows(responseJSON)
});
})
.catch((error) => {
console.error(error);
})
}
Это заполняет источник данных. Данные уже проанализированы в JSON, так как мы используем response.json(). Здесь мы используем fetch.
Теперь нам нужно определить, как должен выглядеть каждый элемент. Для этого мы определяем новый файл с именем MovieItem.js. Примечание: имя должно начинаться с заглавной буквы.
Мы определяем элемент следующим образом
import React,{Component,PropTypes} from 'react';
import {StyleSheet,Text,View,Image,
TouchableHighlight} from 'react-native';
export default class MovieItem extends Component {
_onPressItem = ()=> {
console.log("Pressed "+this.props.name);
}
render(){
return(
<TouchableHighlight
style={{flex:1}}
onPress = {this._onPressItem}
>
<View style= {styles.mainContainer}>
<Image
style= {styles.imageView}
source = {{uri: 'https:'+this.props.movieURL}}>
</Image>
<Text style= {styles.textView}>{this.props.name}
</Text>
</View>
</TouchableHighlight>
);
}
}
MovieItem.propTypes = {
name: PropTypes.string.isRequired,
movieURL: PropTypes.string.isRequired
};
var styles = StyleSheet.create({
mainContainer:{
flexDirection: 'row',
flex: 1,
marginBottom: 10
},
imageView: {
width: 90,
height: 120
},
textView: {
flex:1,
fontSize: 18,
marginLeft: 10,
alignSelf: 'center',
color: '#FFFFFF'
}
})
В этом файле мы определяем класс по умолчанию, который возвращает структуру того, как должен выглядеть элемент. Мы определяем необходимые proptypes, такие как name и MovieURL. Мы используем TouchableHightlight, чтобы сделать представление сенсорным.
Теперь вернемся к нашему файлу index.android.js.
Здесь мы должны импортировать класс MovieItem. Мы делаем это, вставляя следующий оператор после операторов импорта
import MovieItem from './MovieItem';
Обратите внимание, что объект MovieItem также имеет заглавную букву. JSX работает только тогда, когда имя объекта написано с заглавной буквы, иначе будет возвращена ошибка.
Теперь мы определяем функцию, которая берет одну строку данных из всего repsponeJSON, полученного нами из fetch, извлекает имя и URL-адрес и передает их объекту MovieItem.
Мы определяем функцию _renderRow внутри основного класса, чтобы выполнить задачу, как показано ниже.
_renderRow=(rowData)=> {
return (
<MovieItem
name = {rowData.name}
movieURL = {rowData.thumbnail_url}>
</MovieItem>
);
}
Теперь мы модифицируем ListView, чтобы добавить несколько реквизитов.
<ListView
style={{backgroundColor: '#000000'}}
dataSource={this.state.dataSource}
renderRow={(rowData) => this._renderRow(rowData)}
enableEmptySections = {true}
pageSize = {5}
renderSeparator={(sectionID, rowID) =>
<View key={`${sectionID}-${rowID}`} style={{
height: 1,
backgroundColor: 'grey',
marginBottom: 10
}} />
Мы используем renderSeperator, чтобы показать отличительную черту после каждого элемента.
И, наконец, мы вызываем функцию refreshData в конструкторе. Поэтому мы модифицируем конструктор следующим образом:
constructor() {
super();
this._refreshData();
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows([])
};
}
Посмотреть весь проект можно здесь.
Весь файл index.android.js содержит дополнительные функции, такие как renderHeader и renderFooter, для отображения верхнего и нижнего колонтитула в ListView.
Весь файл index.android.js
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
ListView,
Image
} from 'react-native';
import MovieItem from './MovieItem';
export default class MovieList extends Component {
constructor() {
super();
this._refreshData();
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows([])
};
}
_renderRow=(rowData)=> {
return (
<MovieItem
name = {rowData.name}
movieURL = {rowData.thumbnail_url}>
</MovieItem>
);
}
_refreshData = ()=> {
console.log('refresh data was called');
var endpoint = "";
fetch(endpoint)
.then((response)=> response.json())
.then((responseJSON)=>{
this.setState({
dataSource: this.state.dataSource.cloneWithRows(responseJSON)
});
})
.catch((error) => {
console.error(error);
})
}
_componentDidMount = ()=> {
console.log('component did mount was called');
this._refreshData();
}
_renderHeader = ()=> {
return(
<View
style={{
height: 80
}}>
<Text
style= {{
alignSelf: 'center',
color: '#ffffff',
style: 'bold',
fontSize: 24
}}>
Movies
</Text>
</View>
);
}
_renderFooter = ()=> {
<View>
<Text>
Data from API
</Text>
</View>
}
render() {
return (
<ListView
style={{backgroundColor: '#000000'}}
dataSource={this.state.dataSource}
renderRow={(rowData) => this._renderRow(rowData)}
renderHeader = {this._renderHeader}
renderFooter = {this._renderFooter}
enableEmptySections = {true}
pageSize = {5}
removeClippedSubviews = {true}
renderSeparator={(sectionID, rowID) =>
<View key={`${sectionID}-${rowID}`} style={{
height: 1,
backgroundColor: 'grey',
marginBottom: 10
}} />
}
/>
);
}
}
AppRegistry.registerComponent('MovieList', () => MovieList);