RN Review

664 阅读3分钟

一、需求描述:一个页面渲染两个列表,整体页面滚动。没有数据单独显示noData。

流程与信息中心 > RN Review > 需求.png

1. 不推荐:一个ScrollView包裹两个FlatList。不推荐原因:同时使用两个FlatList性能不好,加载慢;如果涉及到分页,用ScrollView包裹会和下拉加载、上拉刷新滚动事件发生冲突,造成下拉加载、上拉刷新无效。

<ScrollView>
    <FlatList
        style={{flex: 1}}
        data={effectiveList}
        keyExtractor={(item, index) =>item.shopName + index}
        renderItem={this._renderItem}
        />
    <FlatList
        style={{flex: 1}}
        data={expiredList}
        keyExtractor={(item, index) =>item.shopName + index}
        renderItem={this._renderItemExpired}
        />
</ScrollView>

2. 推荐一:使用SectionList渲染多个列表,去掉ScrollView。

// 数据源,effectiveList和expiredList为数组,data是SectionList渲染renderItem时自动匹配的字段名,title和effect是任意取的
const totalData = [
    {
        title: effectiveList.length,
        data: effectiveList,
        effect: true,
    },
    {
        title: expiredList.length,
        data: expiredList,
        effect: false,
    },
];

<SectionList
    style={{flex: 1}}
    sections={totalData} 
    keyExtractor={(item, index) => item.shopName + index} 
    // item 读取数据源data的值,可以从section里面读取到其他字段用于判断
    renderItem={({item, section: {title, effect}}) => (
        // 空标签可以用来包裹
        <>
            <ShopItemComponent shopInfo={item} isExpired={effect ? true : false}/>
         // 根据数据源长度是否为0,判断是否展示空数据组件
            {title === 0 && (
            <EmptyView emptyContent={I18n.t('dcr_shop_visit_no_record')}/>
            )}
        </>
    )}
    // 渲染标题
    renderSectionHeader={({section: {title}}) => (
        <View>
         <Text style={styles.titleText}>{title}</Text>
        </View>
    )}
/>


// Store.ts
// 注意使用SectionList的数据源要做一层特殊处理:
@observable public effectiveList: Array<ListResponse> = []; 
@computed get formattedEffectiveList() {
    return this.effectiveList
        .map(v => {
        return {
            id: v.id,
            shopName: v.shopName,
            shopCode: v.shopCode,
            brand: v.brand,
            address: v.address,
            planDate: v.planDate,
            planExecutor: v.planExecutor,
        };
    })
        .slice(); // And slice the listData
}
// 页面使用时:
const effectiveList = this.props.Store.formattedEffectiveList;

3. 推荐二:可以使用一个FlatList,一个列表写在ListHeaderComponent(头部组件)中,一个列表写在renderItem中,还可以写在ListFooterComponent(尾部组件)中。(灵活使用)

4. 推荐三:两个列表的数据在一个FlatList渲染,渲染时根据数据源不同,展示不一样的效果。

二、 <React.Fragment> 与 <> </>

1. 代码中使用<React.Fragment>:需求为一个组件返回多个元素,Fragment可以聚合一个子元素列表,并且不在DOM中增加额外节点。

// 示例一:
class Columns extends React.Component {
  render() {
    return (
      <React.Fragment>
        <td>Hello</td>
        <td>World</td>
      </React.Fragment>
    );
  }
}
// 示例二:
function Glossary(props) {
  return (
    <dl>
      {props.items.map(item => (
        // 没有`key`,将会触发一个key警告
        <React.Fragment key={item.id}>
          <dt>{item.term}</dt>
          <dd>{item.description}</dd>
        </React.Fragment>
      ))}
    </dl>
  );
}

2. 与之类似的也可以用空标签来聚合:<></>

render() {
  return (
    <>
      <ChildA />
      <ChildB />
      <ChildC />
    </>
  );
}

3. 区别:Fragment可以带key,<></> 语法不能接受键值或属性。<></> 是 <React.Fragment/> 的语法糖。

三、SafeAreaView 兼容刘海屏。

1. 页面代码用 包起来,设置一个flex: 1的样式。

<SafeAreaView style={{flex: 1}}>
 <Text>Page content</Text>
</SafeAreaView>

四、触摸组件(TouchableHighlight、TouchableOpacity、TouchableWithoutFeedback)。

1. 可以用于包裹按钮、可点击的列表等。

<TouchableOpacity style={{flex: 1}} onPress={()=> this._confirm()} >
 <Text style={[styles.btnText, styles.bgBlue]}>确认</Text>
</TouchableOpacity>

2. 区别:TouchableHighlight(高亮触摸)、TouchableOpacity(透明触摸)、TouchableWithoutFeedback(无视觉变化)。

五、flex: 1;

    1. flex: 1; 表示组件可以撑满父组件所有剩余空间。
    1. 如果同时存在多个并列的子组件,都设为flex: 1; 则均分。
    1. 父组件设置了flex: 1; 占满整个窗口,子组件撑满父组件。
    1. 如果值不一样,谁设置的值大,占据剩余空间就大(权重)。

六、优化。

1. 自定义的有状态组件尽量继承自PureComponent,这样系统会自动在shouldComponentUpdate中默认做一层浅比较(直接拿两个对象做比较,对象中的子元素不做比较),可以减少一些不必要的渲染。Component是每次setState都会去render, 即使你setState的值和之前相同,也会render。

class Son extends React.PureComponent{}

2. 对于同层级的相同类型的组件,要给每个组件指定唯一的key值。

3. 尽量减少state的数据。

4. 使用shouldComponentUpdate生命周期方法。在生命周期中判断两个值是否相同,如果不同,就返回true,组件更新,如果相同,返回false。组件不更新。

shouldComponentUpdate(nextProps){
    return this.props.iNum !== nextProps.iNum
}