目標:簡化及規範SectionList的使用。
實現:基於SectionList的封裝。
適用的情況
普通的線佈局+列表/分類列表+普通線程佈局+列表/分類列表。如下圖:
封裝後的BaseSectionList.js類,如下:
/**
* @desc
* @author MaRui
*/
import {
View,
StyleSheet,
SectionList
} from 'react-native';
import React, {Component} from "react";
// [
// {data: [], layoutType: 0, title: {}}
// ]
export default class BaseSectionList extends Component {
constructor(props) {
super(props);
this.state = {
dataList: []
};
}
componentDidMount() {
if (!this.props.data) {
return
}
let newDataList = [];
if (this.props.data && React.Children.toArray(this.props.children).length !== this.props.length) {
this.setState({dataList: []});
return;
}
for (let i = 0; i < this.props.data.length; i++) {
if (this.props.data[i].length === 0) {
newDataList.splice(newDataList.length, 0, {title: {}, data: [], layoutType: i});
continue
}
for (let j = 0; j < this.props.data[i].length; j++) {
let datumElement = this.props.data[i][j];
if (datumElement) {
let title = this.props.data[j].title;
let data = this.props.data[j].content;
newDataList.splice(newDataList.length, 0, {title: title, data: data, layoutType: i});
} else {
newDataList.splice(newDataList.length, 0, {title: {}, data: [], layoutType: i});
}
}
}
this.setState({dataList: newDataList});
}
componentWillReceiveProps(nextProps) {
if (!this.props.data) {
return
}
let isRefresh = false;
if (this.props.data !== nextProps.data && this.props.data.length !== nextProps.data.length) {
isRefresh = true;
} else {
for (let i = 0; i < this.props.data.length; i++) {
if (this.props.data[i] !== nextProps.data[i]) {
isRefresh = true;
break;
}
}
}
if (isRefresh) {
let newDataList = [];
for (let i = 0; i < nextProps.data.length; i++) {
if (nextProps.data[i].length === 1 && nextProps.data[i][0].baseSectionListType === 'baseSectionListContent') {
newDataList.splice(newDataList.length, 0, {title: {}, data: [], layoutType: i});
continue
}
for (let j = 0; j < nextProps.data[i].length; j++) {
let datumElement = nextProps.data[i][j];
if (datumElement) {
let title = datumElement.title;
let data = datumElement.content;
newDataList.splice(newDataList.length, 0, {title: title, data: data, layoutType: i});
} else {
newDataList.splice(newDataList.length, 0, {title: {}, data: [], layoutType: i});
}
}
}
this.setState({dataList: newDataList}, () => {
// alert('值:'+JSON.stringify(this.state.dataList)+'\n個數:'+this.state.dataList.length);
});
}
}
//分組創建的cell
cellView = (data) => {
if (data && this.props.children) {
if (React.Children.toArray(this.props.children)[data.section.layoutType].props.renderCell) {
return React.Children.toArray(this.props.children)[data.section.layoutType].props.renderCell(data)
}
// 建議數據少時使用
if (React.Children.toArray(this.props.children)[data.section.layoutType].props.renderCellIsShow && !this.props.itemIsShow) {
return React.Children.toArray(this.props.children)[data.section.layoutType].props.renderCellIsShow(data)
}
}
return <View/>
};
//列表分組的header
headerView = (data) => {
if (data && this.props.children) {
if (React.Children.toArray(this.props.children)[data.section.layoutType].props.renderHeader) {
return React.Children.toArray(this.props.children)[data.section.layoutType].props.renderHeader(data)
}
// 建議數據少時使用
if (React.Children.toArray(this.props.children)[data.section.layoutType].props.renderHeaderIsShow && !this.props.itemIsShow) {
return React.Children.toArray(this.props.children)[data.section.layoutType].props.renderHeaderIsShow(data)
}
}
return <View/>
};
extraUniqueKey = (item, index) => {
return index + item;
};
render() {
return (
<View style={[{flex: 1}]}>
<View style={styles.container}>
<SectionList
{...this.props}
sections={this.state.dataList}
renderItem={this.cellView}
keyExtractor={this.extraUniqueKey}
renderSectionHeader={this.headerView}
scrollEnabled={true}
stickySectionHeadersEnabled={false}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1
},
});
主要實現了,不用給每一塊的數據做標記就可以實現分類列表的顯示。之後補充封裝代碼的解析
BaseSectionList的使用,實現上圖如下:
佈局:
<BaseSectionList
// 此處可以添加設置SectionList的任何屬性,比如頭部佈局、底部佈局、分割線等等
style={{flex: 1}}
data={this.props.data}
ListHeaderComponent={this.headerRender}>
<View
renderCell={this.renderListCell}/>
<View
renderHeader={this.renderTitle}/>
<View
renderHeader={this.renderComment}
renderCell={this.renderReply}/>
</BaseSectionList>
其中佈局1爲普通佈局可以設置爲ListHeaderComponent。此後,每一塊佈局都添加一個子view,234三塊佈局就添加三個標籤“<View/>”。具體設置:比如佈局2是列表則添加一個子view只設置renderCell,renderCell是list的item的佈局。佈局3是一個線性佈局,可設置renderHeader即可。佈局4是一個二級列表即分類列表,需要同時設置renderHeader和renderCell分別代表分類標題佈局和分類列表item的佈局。
數據結構:
let data = [];
data.splice(0, 0, [{title: {}, content: []}]);
data.splice(1, 0, [{baseSectionListType: 'baseSectionListContent'}]);
data.splice(2, 0, [{title: {'課程'}, content: ['語文', '數學', '英語']},
{title: {'班級'}, content: ['一班', '二班', '三班']},
{title: {'老師'}, content: ['張老師', '王老師', '李老師']}]);
傳輸的數據是一個數組。上邊添加的三個佈局,則數組的長度必須是3。每個元素也必須是數組,該例子的格式即是 [ [ ], [ ], [ ] ]。下面說每個元素的填充。分以下三種情況:
- 模塊是列表,比如:佈局2
元素是一個數組,長度是1。[{title: {}, content: []}]。title設置爲空對象,content傳數組即該塊列表對應的數組。 - 模塊是線性佈局,比如:模塊3
元素是一個數組,長度是1, 元素是{baseSectionListType: ‘baseSectionListContent’}。這個是固定不變的,實際只起到佔位符的作用。該模塊的數據自己管理。 - 模塊是分類列表,比如:模塊4
元素是一個數組,數組的長度是分類列表的類的個數,比如有3類[{title: {}, content: []}, {title: {}, content: []}, {title: {}, content: []}]。title是標題對應的數據,content傳數組即該塊列表對應的數組。