React Native从网络拉取数据并填充列表

Posted by 共田君的博客 on November 12, 2016

#React Native从网络拉取数据并填充列表

##本文工作,涉及到以下几个知识点

  • 请求网络并得到数据
  • 创建Button并触发按钮事件测试网络
  • 创建列表listview
  • 创建CELL
  • 根据返回的数据填充CELL

使用Fetch请求网络

React Native提供了和web标准一致的Fetch API,用于满足开发者访问网络的需求。如果你之前使用过XMLHttpRequest(即俗称的ajax)或是其他的网络API,那么Fetch用起来将会相当容易上手。这篇文档只会列出Fetch的基本用法,并不会讲述太多细节,你可以使用你喜欢的搜索引擎去搜索fetch api关键字以了解更多信息。

发起网络请求

要从任意地址获取内容的话,只需简单地将网址作为参数传递给fetch方法即可(fetch这个词本身也就是获取的意思):

fetch('https://mywebsite.com/mydata.json')

Fetch还有可选的第二个参数,可以用来定制HTTP请求一些参数。你可以指定header参数,或是指定使用POST方法,又或是提交数据等等: 提供两个可用的url,返回json http://facebook.github.io/react-native/movies.json http://bbs.reactnative.cn/api/category/3 可以替换下面的url

fetch('https://mywebsite.com/endpoint/', {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    firstParam: 'yourValue',
    secondParam: 'yourOtherValue',
  })
})

TIPS:本人用的是xcode8做测试,遇到网络请求不通的情况,解决方法是在info.plist加入一项允许http的请求,这是由于兼容ios9+的一种方式,通过看控制台的log知道访问的结果. 查看测试结果也可以利用工具Charles

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>NSAllowsArbitraryLoads</key>
	<true/>
</dict>
</plist>


创建按钮并触发事件 TouchableHighlight

由于RN的特点,没有直接对Button封装出来,但是有可用的可点击组件,会相应点击事件。 中文网TouchableHightlight介绍 本组件用于封装视图,使其可以正确响应触摸操作。当按下的时候,封装的视图的不透明度会降低,同时会有一个底层的颜色透过而被用户看到,使得视图变暗或变亮。在底层实现上,实际会创建一个新的视图到视图层级中,如果使用的方法不正确,有时候会导致一些不希望出现的视觉效果。譬如没有给视图的backgroundColor显式声明一个不透明的颜色。

例子:

renderButton: function() {
  return (
    <TouchableHighlight onPress={this._onPressButton}>
      <Image
        style={styles.button}
        source={require('./button.png')}
      />
    </TouchableHighlight>
  );
},

注意:TouchableHighlight只支持一个子节点

如果你希望包含多个子组件,用一个View来包装它们。

所以结合上面的网络请求,我们的按钮触发事件可以这样写

  buttonTap=()=>{ 

    fetch( 'http://bbs.reactnative.cn/api/category/3')
   .then((response)=>response.json())
    .then((jsondata) =>{
        console.log(jsondata); 
        const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
   
        this.setState({dataSource: ds.cloneWithRows(jsondata.topics)});
        this.setState({title:jsondata.description});
         //alert(jsondata);
    })
    .catch((error)=>{
      alert(error);
      console.warning(error);
    });
  };

按钮的布局代码

  render() {
    return (
      <View style={[styles.container,{paddingTop: 22}]}>
          <TouchableHighlight style= onPress={this.buttonTap} >
          <Image
            style={styles.button}
            source={require('./img/favicon.png')}
          />
    );
}

###创建ListView ListView组件用于显示一个垂直的滚动列表,其中的元素之间结构近似而仅数据不同。

ListView更适于长列表数据,且元素个数可以增删。和ScrollView不同的是,ListView并不立即渲染所有元素,而是优先渲染屏幕上可见的元素。

ListView组件必须的两个属性是dataSourcerenderRowdataSource是列表的数据源,而renderRow则逐个解析数据源中的数据,然后返回一个设定好格式的组件来渲染。

下面的例子创建了一个简单的ListView,并预设了一些模拟数据。首先是初始化ListView所需的dataSource,其中的每一项(行)数据之后都在renderRow中被渲染成了Text组件,最后构成整个ListView

rowHasChanged函数也是ListView的必需属性。这里我们只是简单的比较两行数据是否是同一个数据(===符号只比较基本类型数据的值,和引用类型的地址)来判断某行数据是否变化了。

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

class ListViewBasics extends Component {
  // 初始化模拟数据
  constructor(props) {
    super(props);
    const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
    this.state = {
      dataSource: ds.cloneWithRows([
        'John', 'Joel', 'James', 'Jimmy', 'Jackson', 'Jillian', 'Julie', 'Devin'
      ])
    };
  }
  render() {
    return (
      <View style=>
        <ListView
          dataSource={this.state.dataSource}
          renderRow={(rowData) => <Text>{rowData}</Text>}
        />
      </View>
    );
  }
}

// 注册应用(registerComponent)后才能正确渲染
// 注意:只把应用作为一个整体注册一次,而不是每个组件/模块都注册
AppRegistry.registerComponent('ListViewBasics', () => ListViewBasics);

以上介绍了一个简单的ListView ,然而现实中的CELL并没有如此简单,我们需要重构CELL的组织方式


创建展示的CELL

如果学过RN的布局方式,定制化CELL就是个简单的事情,此处需要用到alighitmes,justifycontent,具体的可以参考 React Native 的布局方式flextbox

直接给出代码


class CELL extends Component{

  constructor(props){
    super(props);
    this.state = { detailTitle:'aaaa'};
  }
  render(){
    return(
          <View style=>
              <View style= >
                <Text style= >{this.props.title}</Text>
                <Text style=>{this.props.detailTitle}</Text>
              </View>
              <View style=></View>
          </View>
      );
  }
}

要实现以上的需求,效果图如下

Paste_Image.png

  • 大伙可以先不看代码,自己组织代码试一下!
  • 有问题欢迎在下面留言
  • 不要吝啬自己的喜欢,有用的话点个赞吧

源代码如下:

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


class RNCSDemo extends Component {
  // 初始化模拟数据
  constructor(props) {
    super(props);
    const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
    this.state = {
      dataSource: ds.cloneWithRows([
        // 'John', 'Joel', 'James', 'Jimmy', 'Jackson', 'Jillian', 'Julie', 'Devin'
      ])
    };
    this.buttonTap();//初始化
  }

 getMoviesFromApiAsync() {
    return fetch('http://facebook.github.io/react-native/movies.json',
      {
        headers:{
          'Accept': 'application/json',  
          'Content-Type':'application/json', 
        }
      })
      .then((response) => response.json())
      .then((responseJson) => {
        return responseJson.movies;
      })
      .catch((error) => {
        console.error(error);
      });
  }

  buttonTap=()=>{ 

    fetch( 'http://bbs.reactnative.cn/api/category/3'
      // , {
        // method: 'GET',
        // headers: {
          // 'Accept': 'application/json',
          // 'Content-Type': 'application/json',
        // },
        // body: JSON.stringify({ 
        // } )
    // }
    ).then((response)=>response.json())
    .then((jsondata) =>{
        console.log(jsondata); 
        const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
   
        this.setState({dataSource: ds.cloneWithRows(jsondata.topics)});
        this.setState({title:jsondata.description});
         //alert(jsondata);
    })
    .catch((error)=>{
      alert(error);
      console.warning(error);
    });

  };
  render() {

    return (
      <View style={[styles.container,{paddingTop: 22}]}>
          <TouchableHighlight style= onPress={this.buttonTap} >
          <Image
            style={styles.button}
            source={require('./img/favicon.png')}
          />
        </TouchableHighlight>
        <Text  style= >{this.state.title}</Text>
        <ListView style=
          dataSource={this.state.dataSource}
          renderRow={(rowData) => <CELL title={rowData.title} detailTitle={rowData.timestampISO}></CELL>}
        />
      </View>
    );
}
}

class CELL extends Component{

  constructor(props){
    super(props);
    this.state = { detailTitle:'aaaa'};
  }
  render(){
    return(
          <View style=>
              <View style= >
                <Text style= >{this.props.title}</Text>
                <Text style=>{this.props.detailTitle}</Text>
              </View>
              <View style=></View>
          </View>
      );
  }
}

const styles= StyleSheet.create({ 

  container:{flex:1,
    justifyContent:'center',
    backgroundColor: '#F5FC00',}
});


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