Давайте создадим нативное приложение 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);