【React Native】我遇到的坑

743 查看

  最近学习了一下facebook出的React Native,本来对这个技术十分不屑,认为最多只会在facebook内部流行,但是主导的learn once, write everywhere深深地吸引住了我。
  平常学习通常总结很少,因为网上到处都是资料,需要的时候查一下就好了,但是React Native尚在成长期,不管是国内国外资料文档都不全,遇到问题的时候也很难查到想要的资源,所以不得不把我遇到坑总结一下。


render

  React Native整个UI就是一棵Component的树,在Component生命周期中render作为渲染整个Component的方法,在Component的state发生改变时,就会回调这个方法,重新渲染整个Component。


Component树

没有必要的渲染

TouchableOpacity

  一般的 ListView 中每个 Row 的点击会对应一个 onPress 的事件,例如 push 到下一个 View,这种情况使用 onPress={this.handlePress} ,然后在 handlePress里进行响应的操作就 OK 了。
  在显示 Github 时间线的时候,每个 Row 里会提到多个用户名和仓库名,点击用户名和仓库名时需要做不同的处理。最容易想到的方法就是给 handlePress传递参数了,如果使用下面的代码:

<View style={styles.action}> 
  <Text>{actionDescription}</Text> 
  <TouchableOpacity onPress={this.goToUser.bind(this,this.props.data.name)}>
    <Text>data.payload.member.login</Text>
  </TouchableOpacity> 
<Text> to </Text>

  在执行goToUser方法后,render会被再次执行,因为Component的state发生了改变,这样其实就在没有必要的情况下重新渲染了一遍Component,因为其实Component的数据源其实并没有发生改变。
  解决办法:

shouldComponentUpdate(nextProps, nextState) {
    return nextProps.data !== this.props.data;
}

  在shouldComponentUpdate方法中返回Component是够应该被更新。

Navigator

  大多数时候我们都需要导航器来应对不同场景(页面)间的切换。它通过路由对象来分辨不同的场景,我们这里采用的就是renderScene方法,根据指定的路由来渲染。
具体的使用方法不再赘述,可以看看官方文档

问题所在:

Navigator的动画是由JavaScript线程所控制的。想象一下“从右边推入”这个场景的切换:每一帧中,新的场景从右向左移动,从屏幕右边缘开始(不妨认为是320单位宽的的x轴偏移),最终移动到x轴偏移为0的屏幕位置。切换过程中的每一帧,JavaScript线程都需要发送一个新的x轴偏移量给主线程。如果JavaScript线程卡住了,它就无法处理这项事情,因而这一帧就无法更新,动画就被卡住了。

  解决办法:

import React, { Component } from 'react';
import {
  ...
  InteractionManager,
} from 'react-native';
componentDidMount(){
   InteractionManager.runAfterInteractions(() => {
     this.fetchData();
   });
}

  大概的意思就是等待navigator切换动画完成后,在执行fetchData方法。

ListView

  在整个项目中,可能列表是我们最常用的Component,但是ListView是我觉得React Native最坑组件,因为它居然没有回收机制,在Xcode中查看Memory,在不断加载数据后,Memory持续上升。


ListView Memory

  解决办法
  react-native-sglistview可能是目前来说相对比较好的解决办法,只渲染当前出现的cell,但是我发现如果滑动速度比较快的情况下会出现来不及渲染而白屏。

Props

  在Component切换或者子Component通常通过props传值,但是传入的props值是不允许改变的,如果需要改变传入值,那你就只能把props值传入state中,然后在需要Component使用this.state....来获取该值,在需要update的地方this.setState。

constructor(props){
   super(props);
   this.state = {
      data:props.data,
   };
}

numberOfLines

  在Text Component中我们可以通过设置numberOfLines来控制Text展示行数。以下代码就是控制Text展示行数在5行或5行以内,如果不设置numberOfLines,那么Text默认有多少展示多少。

<Text numberOfLines={5}>{'在Text Component中我们可以通过设置numberOfLines来控制Text展示行数。'}</Text>

  有的时候我们需要进行行数变化切换,就像朋友圈那样,刚开始是收起来的,点击展开后,展示全部文字。如果在iOS中直接把numberOfLines直接设置为0,那么就相当于默认设置,展示全部文字,但是在Android中就真的是0行了......
  这里我觉得解决办法是设置一个比较大的数:

var isOpen = true;
this.setState({
  textLine:isOpen?1000:3,
});
<Text numberOfLines={this.state.textLine}>{'在Text Component中我们可以通过设置numberOfLines来控制Text展示行数。'}</Text>

  
不正确的地方还望指正......
持续更新......