react-native(七)数组元素的渲染输出

2017-11-08 08:10:27 react-native 6381 1

功能:输出列表数据

计算用于动态渲染的容器高度,避免界面显示不出来

├─── 计算外盒高度  
 └─── 计算单个内部元素的高度、宽度  
    └─── 计算渲染一行,需要几个内部元素  
      └─── 计算当前元素总数,所需行数,以计算外盒所需高度  
性能考虑

因为状态会被记录,所以,内存会越耗越多,处理这个的方法就是
保持被渲染部分的数据长度恒定,往回拉的时候,用下拉刷新重新请求
目前能实现这个的,就是rn 0.43之后的版本了
这个版本组件名为 FlatList

示例:渲染数组

渲染文字类,单花括号

// 数组数据
let pics = [
  "https://i.loli.net/2017/11/08/5a02a2733777b.jpg",
  "https://i.loli.net/2017/11/08/5a02a27aa1473.jpg",
];
let jsxArr = []; // 装新生成的视图的容器
// 遍历数组,赋值到
pics.forEach((val, index)=> {
    jsxArr.push(
      <Text key={index}>{val}</Text>
    );
});
return (
  <View>  
    {jsxArr}  
  </View>  
);

但是如果是 Image ,需要注意

pics.forEach((val, index)=> {
  // 避免赋值遇到双花括号,提前将数据放到临时变量中
  let temp_obj = {}; // 这个不能在forEach外面单独声明,否则只能显示一条数据
  let key_name = 'img_' + index;
  temp_obj.uri = val;
    jsxArr.push(
        <Image
          key={key_name}
          source={temp_obj}
          style={sscStyle.upload_do}
        ></Image>
    );
});

注意代码中的 key={index} 一定要给元素加上,不然会报一个警告,是因为

列表中React需要我们为的每个元素提供一个唯一的“key”标识符,使得React能够更智能的重用一个组件(因为DOM的创建是非常耗时的),从而提高性能

可能会用到

react-native中key、ref、bind的作用和使用

应用:写一个上拉刷新功能

视图层

// 基础引入
import React, { Component } from 'react';
import {
  View,
  ScrollView,
  Image,
  Text,
} from 'react-native';
// 当前页面的一些逻辑  ---- 逻辑层
import sscLogic from './logic/PayLogScreen';

export default class PayLogScreen extends Component{
  constructor(p){
    super(p);
    this.logic = new sscLogic(this);
    this.state = {
      data:{
        info:[],  // 数据列表
        page_count:1, // 数据分页属于第几页
      },
      loading:true, // loading层开关

    };
    this.logic.get_goods_list(); // 首次加载
  }

  // 充值记录 ---- 填充数据
  log_tpl(){
    let it = this;
    let jsxArr = []; // 装生成新的视图的容器
    // 遍历数组,赋值到
    if( it.state.data.info.length!==0 ){
      it.state.data.info.forEach((val, index)=> {
          jsxArr.push(
            it.log_list_tpl(val)
          );
      });
    }else{
      return it.no_data();
    }
    return jsxArr;
  }
  // 充值记录 ---- 模板
  log_list_tpl(d){
    let it = this;
    return (
      <View style={sscStyle.log_list_tpl} key={d.id}>
        <View style={sscStyle.log_list_tpl_title_view}>
          <Text style={sscStyle.log_list_tpl_title_left_text}>消费金额</Text>
          <Text style={sscStyle.log_list_tpl_title_right_text}>¥{it.logic.format_money(d.total)}</Text>
        </View>
        <View style={sscStyle.log_list_tpl_container}>
          <View style={sscStyle.log_list_tpl_container_view}>
            <Text style={sscStyle.log_list_tpl_container_left_text}>支付渠道</Text>
            <Text style={sscStyle.log_list_tpl_container_right_text}>{it.logic.get_pay_channel(d.channel)}</Text>
          </View>
          <View style={sscStyle.log_list_tpl_container_view}>
            <Text style={sscStyle.log_list_tpl_container_left_text}>交易时间</Text>
            <Text style={sscStyle.log_list_tpl_container_right_text}>{d.time}</Text>
          </View>
        </View>
        {this.____separate()}
      </View>
    );
  }
  // 无数据页 ---- 视图
  no_data(){
    return(
        <ScrollView style={sscStyle.container}>
          <View style={sscStyle.no_data}>
            <Image
                source={require('./img/nothing.png')}
                style={sscStyle.no_data_pic}
            ></Image>
            <Text style={sscStyle.no_info}>暂时还没有支付记录哟</Text>
          </View>
        </ScrollView>
    );
  }
  // loading层 ---- 视图
  ____loading(){
    if( this.state.loading===false ){
      // loading层模板,gScreen.width就是获取到的设备的宽度
      return(
        <View style={{width:gScreen.width}}>
          <Image
            source={require('./img/loading.gif')}
            style={{width:45,height:15,marginTop:20,marginBottom:20,alignSelf:'center',}}
          ></Image>
        </View>
      );
    }else{
      return(<View style={{width:0,height:0,}}></View>)
    }
  }

  // 渲染
  render() {
    if(this.state.data.page_count===0){
      return this.no_data();
    }else {
      return (
          <ScrollView 
            style={sscStyle.container} 
            onScroll={(d)=>{this.logic.if_scroll_to_bottom(d)}} // 监听滑动事件
          >
            {this.log_tpl()}
            {this.____loading()}
          </ScrollView>
      );
    }
  }
}

逻辑层

import toastModule from '../third/toastModule'; // 反正是弹出临时消息用的
export default class sscLogic {
  constructor(pointer){
    this.p = pointer ; // 保存 视图层指针
    // 分页相关
    this.page_now = 1;
    this.page_lock = false; // 防止重复加载
  }
  //++++++++++++++++++++++++++++++++++++++++++++++++++
  //                  基础功能
  //++++++++++++++++++++++++++++++++++++++++++++++++++

    // 判断是否滑动到底部
  if_scroll_to_bottom(e: Object) {
    let offsetY = e.nativeEvent.contentOffset.y; //滑动距离
    let contentSizeHeight = e.nativeEvent.contentSize.height; //scrollView contentSize高度
    let oriageScrollHeight = e.nativeEvent.layoutMeasurement.height; //scrollView高度
    // contentSizeHeight 减10是为了防止底部loading层导致不能正常被拉取到
    if (parseInt(offsetY + oriageScrollHeight) >= parseInt(contentSizeHeight) - 10) { 
      tool.log('get_goods_list');
      this.get_goods_list();
    }
  }

  //++++++++++++++++++++++++++++++++++++++++++++++++++
  //                    数据拉取
  //++++++++++++++++++++++++++++++++++++++++++++++++++
  // 拉取数据更新视图
  get_goods_list() {

    let get_this = this; // 保存 this指针

    // 防止重复请求
    if( get_this.page_lock===false){
      get_this.page_lock = true;
      setTimeout(()=>{
        get_this.page_lock = false;
      },1000);
    }else{
      // 退出
      return;
    }
    tool.get_token(token=>{ // 取出token身份
      let url = http.url('v1/paid/logs'), // 设置请求地址
          to_page = 1,
          request_url = url + '?' + 'token=' + token + '&to_page=' + get_this.page_now;
      console.log("请求地址"+request_url);

      let it = this.p; // 保存 视图层指针
      // 开启loading层
      it.setState(pre=>{
        pre.loading = true;
        return pre;
      });
      http.get(request_url, d=>{ // 拉取数据, d是回调数据
        // 关闭loading层
        it.setState(pre=>{
          pre.loading = false;
          return pre;
        });
        if(0===d.data.page_count){
          it.setState(pre=>{
            pre.data.page_count = 0;
            return pre;
          });
        }
        // 先显示新品,然后一直往后拉。当数量达到极限,则不显示了。
        if( get_this.page_now > d.data.page_count){
          toastModule.run('没有更多了');
          return;
        }
        // 渲染
        it.setState(pre=>{
          pre.data.info = [].concat.apply(pre.data.info, d.data.info); // 追加数据
          return pre;
        });
        get_this.page_now ++; // 增加页数

      });
    });
  }

  // 判断交易方式
  get_pay_channel( type ){
    switch(type){
      case 0:
        return '后台直充';
      case 1:
        return '微信';
      case 2:
        return '支付宝';
      default:
        return '未知';
    }
  }

  /**
  * 将单位分的整型,转换为元的字符串格式
  * @param int money 待格式化的数
  * @return string 
  */
  format_money(money){
    let rel_money = parseInt(money);
    if( isNaN(rel_money)  ){
      rel_money = 0;
    }
    let str_rmb = Math.floor(rel_money/100);
    let str_dot = rel_money % 100;
    // 小于10自动补零
    if( str_dot<10 ) {
      str_dot = "0"+str_dot +"";
    }

    return str_rmb + '.' + str_dot;
  }
}

示例后端数据

{
  "code": 200,
  "detail": "success",
  "data": {
    "info": [{
      "total": 1,
      "channel": 1,
      "time": "2017-10-31 12:01:23"
    }, {
      "total": 1,
      "channel": 1,
      "time": "2017-10-31 11:47:05"
    }, {
      "total": 1,
      "channel": 1,
      "time": "2017-10-31 11:38:45"
    },
    "page_count": 1,
    "total": 1
  }
}
注:若无特殊说明,文章均为云天河原创,请尊重作者劳动成果,转载前请一定要注明出处