react-native 自訂封裝重新整理組件
幾個月沒寫部落格了,最近一直在寫react 和react-native,前幾天剛發了一版基於react-native混合開發的App,這幾天趕快總結下。
寫過java的同學,再去學習react和react-native就會比較容易上手,並且會有一種似曾相識的感覺,不錯,今天我要總結的重新整理功能就和Android原生的實現很類似,在Android原生當中,可以說是家喻戶曉了,在react-native中實現也是一樣的簡單,在rn的升級中,FlatList是listView的升級版,就好比如RecyclerView是listView的升級版一樣。OK, 廢話不多話說。
目錄
1.FlatList常用的屬性方法
2.封裝一個上拉載入,下拉重新整理的組件
1.FlatList常用的屬性方法 data: ?Array 為了簡化起見,data屬性目前只支援普通數組。 renderItem: (info: {item: ItemT, index: number}) => ?React.Element 根據行資料data渲染每一行的組件 onRefresh?: ?() => void 如果設定了此選項,則會在列表頭部添加一個標準的RefreshControl控制項,以便實現“下拉重新整理”的功能。同時你需要正確設定refreshing屬性。 refreshing?: ?boolean 在等待載入新資料時將此屬性設為true,列表就會顯示出一個正在載入的符號。 onEndReachedThreshold?: ?number 決定當距離內容最底部還有多遠時觸發onEndReached回調。注意此參數是一個比值而非像素單位。比如,0.5表示距離內容最底部的距離為當前列表可見長度的一半時觸發。 onEndReached?: ?(info: {distanceFromEnd: number}) => void 當列表被滾動到距離內容最底部不足onEndReachedThreshold的距離時調用。 ItemSeparatorComponent?: ?ReactClass 行與行之間的分隔線組件。不會出現在第一行之前和最後一行之後。? keyExtractor: (item: ItemT, index: number) => string 此函數用於為給定的item產生一個不重複的key。Key的作用是使React能夠區分同類元素的不同個體,以便在重新整理時能夠確定其變化的位置,減少重新渲染的開銷。若不指定此函數,則預設抽取item.key作為key值。若item.key也不存在,則使用數組下標。 ListHeaderComponent?: ?ReactClass 頭部組件 ListFooterComponent?: ?ReactClass 尾部組件 ListEmptyComponent?: ?ReactClass | React.Element 列表為空白時渲染該組件。可以是React Component, 也可以是一個render函數, 或者渲染好的element。
extraData?: any 如果有除data以外的資料用在列表中(不論是用在renderItem還是Header或者Footer中),請在此屬性中指定。同時此資料在修改時也需要先修改其引用地址(比如先複製到一個新的Object或者數組中),然後再修改其值,否則介面很可能不會重新整理。 註:當然還有其他的屬性方法,請自行查閱文檔FlatList 2.封裝一個上拉載入,下拉重新整理的組件 先定義一些propTypes,利於組件的擴充使用
static propTypes = { renderItem: PropTypes.func.isRequired, /* 渲染行的方法 */ ItemSeparatorComponent: PropTypes.func, /* 渲染行間隔的方法 */ keyExtractor: PropTypes.func, /* 返回每行key的方法 */ ListFooterComponent: PropTypes.func, /* 渲染列表底部的方法 */ ListHeaderComponent: PropTypes.func, /* 渲染列表頭部的方法 */ listUrl: PropTypes.string.isRequired, /* 擷取列表的url */ fetchMethod: PropTypes.string, /* 擷取列表的請求方式(預設為get) */ fetchParams: PropTypes.object, /* 擷取列表的請求參數 */ pageSize: PropTypes.number, /* 每頁行數(預設20條) */ formatData: PropTypes.func, /* 返回列表數組的方法 */ _ref: PropTypes.func, /* 擷取組件執行個體屬性 */ auto: PropTypes.bool, /* 是否在載入時自動查詢(預設自動) */ showFooterBoo: PropTypes.bool, /* 是否顯示腳 */ ref: PropTypes.func, /* 擷取組件執行個體 */ };
2.初始化目前狀態state
constructor(props) { super(props); this._fetchUrl = props.listUrl; this._fetchParams = props.fetchParams; this.state = { list: [], /* 列表資料 */ pageNum: 1, /* 當前頁數 */ count: 0, /* 總條數 */ refreshing: false, /* 是否正在載入 */ isEnd: false, /* 是否載入完 */ isNotList: false, /* 是否未查詢到資料 */ isError: false, /* 是否查詢失敗 */ } }
3.FlatList組件屬性封裝 渲染前準備
const { list, refreshing } = this.state; const { renderItem, ItemSeparatorComponent, keyExtractor = (...arg) => arg[1], ListHeaderComponent, extraData = {}, showFooterBoo, } = this.props;
FlatList封裝
<FlatList data={list} renderItem={renderItem} onRefresh={this.onRefresh} refreshing={refreshing} onEndReachedThreshold={0.2} onEndReached={this.onEndReached} ItemSeparatorComponent={ItemSeparatorComponent} keyExtractor={keyExtractor} ListHeaderComponent={this.renderListHeader} ListFooterComponent={showFooterBoo ? this.renderListFooter : null} ListEmptyComponent={this.renderListEmpty} ref={ref => this._listRef = ref} extraData={extraData} />
4.data={list} 中list資料來源於引用組件處的屬性
5.renderItem={renderItem} 中renderItem來源於引用組件的Item布局屬性
6.onRefresh={this.onRefresh} 下拉重新整理布局如下:
/** * 下拉重新整理 * @return {void} */ onRefresh = () => { this.setState({ isEnd: false, }, () => { this.getList(); }); }
/** * 擷取列表 * @return {void} */ getList = (isRefresh = true) => { const { $fetch, fetchMethod = 'get', pageSize = 20, formatData = ({data}) => ({ list: data.records, count: data.total, }), fetchCatch = () => {}, } = this.props; const { list, pageNum, refreshing, isEnd } = this.state; if((refreshing || isEnd) && !isRefresh) return; const fetchPageNum = isRefresh ? 1 : pageNum; let fetchPromise; if(fetchMethod.toLowerCase() === 'get') { fetchPromise = $fetch.get(this._fetchUrl, { size: pageSize, current: fetchPageNum, ...this._fetchParams, }); } else if(fetchMethod.toLowerCase() === 'post') { fetchPromise = $fetch.post(`${this._fetchUrl}`, { ...this._fetchParams, }); } else { throw new Error('method type error! only "get" or "post"'); } this.setState({refreshing: true}); fetchPromise.then(data => { console.log(data); if(!data.success) { this.setState({ isError: true }); return; } const listData = formatData(data); const currentList = isRefresh ? listData.list : list.concat(listData.list); /* 判斷是否查詢到資料 */ if(fetchPageNum === 1 && listData.list.length === 0){ this.setState({ isNotList: true }); } else { this.setState({ isNotList: false }); } /* 如果有資料則添加到列表中 */ if(listData.list.length) { this.setState({ list: currentList, pageNum: fetchPageNum + 1, }); } if(currentList.length >= listData.count) { this.setState({ isEnd: true }); } this.setState({ count: listData.count, }); }).catch(err => { console.log(err.response); if(!data.success) { this.setState({ isError: true }); } fetchCatch(err); }).finally(() => { this.setState({refreshing: false}); }); }
註:get與post可以根據自己的具體需求進行最佳化,其中網路請求使用的fetch。
7.refreshing={refreshing} 中refreshing布爾值,是用來判斷當前是否是出於重新整理狀態的,當處於請求資料的重新整理狀態時置為true。
8.onEndReachedThreshold={0.2} 當滑動比例為0.2時觸發onEndReached方法;onEndReached={this.onEndReached}上拉載入實現,如下:
/** * 上拉載入 * @return {void} */ onEndReached = () => { this.getList(false); }
註:this.getList(false);方法同下拉重新整理,參數false代表上拉載入。
9.ItemSeparatorComponent={ItemSeparatorComponent} 自訂一個分割線,如下:
export const ItemSeparator = props => { const { paddingLeft = px(40), backgroundColor = '#fff', borderColor = '#eee', } = props; return ( <View style={{paddingLeft, backgroundColor}}> <View style={{height: 1, backgroundColor: borderColor}}></View> </View> );};
10.keyExtractor={keyExtractor} 使用當前資料的id即可,如下:
keyExtractor={item => item.id}
11.ListHeaderComponent={this.renderListHeader} 頭部的渲染,代碼如下:
/** * 渲染列表頭部 */ renderListHeader = () => { const { count } = this.state; const { ListHeaderComponent = () => <View></View> } = this.props; return ListHeaderComponent(this.state); }
註:布局根據自己的需求進行定義即可。
12.ListFooterComponent={showFooterBoo ? this.renderListFooter : null} 屬性showFooterBoo布爾值用來控制是否顯示腳,腳的布局定義如下:
/** * 渲染列表底部 * @return {ReactComponent} */ renderListFooter = () => { const { ListFooterComponent = this.defaultListFooter } = this.props; const { isEnd, refreshing } = this.state; return this.defaultListFooter(isEnd, refreshing); }
/** * 列表預設底部組件 * @return {Node} */ defaultListFooter = () => { return ( <FlexView justify="center" align="center" style={styles.listFooterView}> {this.defaultListFooterContent()} </FlexView> ); }
/** * 列表底部顯示內容 * @return {Node} */ defaultListFooterContent = () => { const { isEnd, refreshing, isNotList, isError } = this.state; if(isNotList) { return ( <_Text>未查詢到相關資料。</_Text> ); } if(isEnd) { return [ <FlexItem style={styles.listFooterLine} key={1}/>, <_Text key={2}>沒有更多資料</_Text>, <FlexItem style={styles.listFooterLine} key={3}/>, ]; } if(refreshing) { return ( <_Text>正在載入...</_Text> ); } if(isError) { return ( <_Text>查詢失敗,下拉重新載入。</_Text> ); } return ( <_Text>上拉載入更多</_Text> ); }
註:腳的布局可以根據自己的項目需求變更。
13.ListEmptyComponent={this.renderListEmpty} 空布局,沒有資料,請求出錯顯示,代碼如下:
/** * 列表為空白時顯示的組件 * @return {ReactComponent} */ renderListEmpty = () => { return ( <View> <Text>沒有資料哦。</Text> </View> ); }
14.ref={ref => this._listRef = ref} 當前執行個體 ok,總結就到這裡了,後續我會總結更多的使用組件的~~