一、需求描述:一个页面渲染两个列表,整体页面滚动。没有数据单独显示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;
-
- flex: 1; 表示组件可以撑满父组件所有剩余空间。
-
- 如果同时存在多个并列的子组件,都设为flex: 1; 则均分。
-
- 父组件设置了flex: 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
}