Learn to encapsulate and reduce unnecessary rework.
Before packaging, design the functionality and API that the component should have, and then build on that.
1. Design of the List component
The API should be similar to the official website as much as possible to reduce the learning cost. Provide a single API function, can not one API multiple uses, reduce the chance of pit. There should be a uniform specification, specifying components that can only have one output for the same behavior.
1.1 List Specification and Description
This List contains both FlatList and SectionList.
- A normal list when data is passed, and a grouping list when sections are passed
- Pull-up loading and pull-down refreshing use React-native Spinkit and are not customizable
- You must specify the height of each item, the height of item, the height of section can not be specified, do not specify the height of 0
- Refresh and loading cannot be manually enabled
- You can end the refresh with a callback in onRefresh, or you can end the refresh with a ref, and the load is the same
1.2 the API definition
api | Type specification | API details |
---|---|---|
data | array | Data source, one-dimensional data |
sections | array | When it’s a two-dimensional array of data |
renderItem | ()=> React.ReactElement | Render each item |
ListEmptyComponent | ()=> React.ReactElement | A component that is rendered when the data is empty |
ListFooterComponent | ()=> React.ReactElement | The tail components |
ListHeaderComponent | ()=> React.ReactElement | The head component |
onRefresh | function | Function that is called when the flush occurs, providing a callback, and executing to indicate the end of the flush |
onEndReached | function | When the function called at the bottom is reached, a callback is provided, and execution represents the end of the pull-up |
itemHeight | (index)=>number | The height of each of these terms |
sectionHeight | (index)=>number | The height of each group’s head |
renderSection | ()=> React.ReactElement | Render the head of each group |
loadingCompleted | boolean | Whether the data is complete when the pull-up is loaded |
style | ViewStyle | Overall style |
itemStyle | ViewStyle | The style of each item |
sectionStyle | ViewStyle | The style of each set of heads |
Methods provided
api | The function definitions | Using the details |
---|---|---|
scrollToEnd | list.scrollToEnd(); | Jump to the bottom |
scrollToStart | list.scrollToStart(); | Jump to the top |
scrollToIndex | list.scrollToIndex(index) | Jumps to the specified item |
scrollToSectionIndex | list.scrollToSectionIndex(index) | Jumps to the specified section header |
endRefresh | endRefresh() | The end of the refresh |
endEndReached | endEndReached() | The pull-up loading is complete |
Implemented apis and unimplemented apis
- data
- sections
- renderItem
- ListEmptyComponent
- ListFooterComponent
- ListHeaderComponent
- onRefresh
- onEndReached
- itemHeight
- sectionHeight
- renderSection
- loadingCompleted
- style
- itemStyle
- sectionStyle
- scrollToEnd
- scrollToStart
- scrollToIndex
- scrollToSectionIndex
- endRefresh
- endEndReached
Three, specific code
import React, { forwardRef, useRef, useImperativeHandle } from 'react'
import { LargeList, RefreshHeader, SpinKit } from '.. /.. /.. /.. /modules'
import { View, StyleSheet } from 'react-native'
import { LoadingFooter } from 'react-native-spring-scrollview'
const __Refresh = ((refreshStyle) = > {
let refreshInstance = null
return () = > {
if (refreshInstance === null) {
refreshInstance = class extends RefreshHeader {
render() {
const { size = 20, type = "Circle", color = "red" } = refreshStyle || {}
return (
<View style={{ justifyContent: 'center', alignItems: 'center' }}>
<SpinKit isVisible={true} size={size} type={type} color={color} />
</View>)}}}return refreshInstance
}
})()
const __EndReached = ((endReachedStyle) = > {
let endReachedInstance = null
return () = > {
if (endReachedInstance === null) {
endReachedInstance = class extends LoadingFooter {
render() {
const { size = 20, type = "Circle", color = "red" } = endReachedStyle || {}
return (
<View style={{ justifyContent: 'center', alignItems: 'center' }}>
<SpinKit isVisible={true} size={size} type={type} color={color} />
</View>)}}}return endReachedInstance
}
})()
const getData = (data, sections) = > {
if(! data) data = []else if(! sections) sections = []if (!Array.isArray(data)) throw new Error("Instead of passing in an array, you pass in:".typeof data)
if (data) {
return [{ items: data }]
} else {
return sections || []
}
}
const RenderItem = (section, index, data, renderItem, itemStyle, itemHeight) = > {
return (<View style={StyleSheet.flatten([itemStyle, { height: itemHeight({ section.index }), overflow: 'hidden' }])}>
{renderItem({ item: data[section].items[index], index })}
</View>)}const RenderSection = (section, data, renderSection, sectionStyle, sectionHeight) = > {
return (<View style={StyleSheet.flatten([sectionStyle, { height: sectionHeight(section), overflow: 'hidden' }])}>
{renderSection({ section: data[section], index: section })}
</View>)}const List = forwardRef(({ data, renderItem, ListFooterComponent, ListHeaderComponent, onRefresh, onEndReached, itemHeight, sectionHeight, renderSection, loadingCompleted, style = {}, refreshStyle = {}, endReachedStyle = {} }, ref) = > {
const listRef = useRef(null)
useImperativeHandle(ref, () = > {
return {
...listRef.current || {},
scrollToStart: () = > {
console.log("By going to scrollToStart.")
listRef.current.scrollToIndexPath({ section: 0.row: 0})},scrollToEnd: () = > {
let row = data[data.length - 1].items.length - 1
listRef.current.scrollToIndexPath({ section: data.length - 1, row })
},
scrollToIndex: (index) = > {
if (data.length > 1) {
console.warn("This API works for regular lists, use scrollToSectionIndex for grouped lists.")}if (index >= data[0].length) {
index = data[0].length - 1
}
listRef.current.scrollToIndexPath({ section: 0.row: index })
},
scrollToSectionIndex: (section) = > {
if (section >= data.length) {
section = data.length - 1
}
listRef.current.scrollToIndexPath({ section: section, row: 0}}}})const endRefresh = () = > {
if (listRef && listRef.current) {
listRef.current.endRefresh()
}
}
const _onRefresh = () = > {
onRefresh && onRefresh(endRefresh)
}
const endLoading = () = > {
if (listRef && listRef.current) {
listRef.current.endLoading
}
}
const _onEndReached = () = > {
onEndReached && onEndReached(endLoading)
}
if(! itemHeight) {throw new Error(The height of each item must be specified.)}else if (typeofitemHeight ! = ='function') {
throw new Error("ItemHeight requires a function type, not:".typeof (itemHeight))
}
return (
<LargeList
data={data}
ref={listRef}
style={style}
{. onRefresh && { refreshHeader: __Refresh(refreshStyle), onRefresh: _onRefresh }}
{. onEndReached && { loadingFooter: __EndReached(endReachedStyle), onEndReached: _onEndReached }}
heightForIndexPath={itemHeight}
renderHeader={ListHeaderComponent}
renderFooter={ListFooterComponent}
renderSection={renderSection}
allLoaded={loadingCompleted}
heightForSection={sectionHeight}
renderIndexPath={renderItem} />)})const SwitchEmptyOrList = forwardRef(({
data, //Data, one-dimensional array sections, renderItem, itemStyle, renderSection, sectionStyle, sectionHeight = () =>0,
itemHeight,
ListEmptyComponent = () => null. props }, ref) = > {
const _data = getData(data, sections)
if (_data.length === 0) {
return ListEmptyComponent()
} else {
return (<List
data={_data}
ref={ref}
renderItem={({ section.row}) = >RenderItem(section, row, _data, renderItem, itemStyle, itemHeight)} {... _data.length > 1 && { renderSection, sectionHeight: (section) => RenderSection(section, _data, renderSection, sectionStyle, sectionHeight) }} itemHeight={itemHeight} {... props} />)}})export default SwitchEmptyOrList
Copy the code
Four, simple use
function TestList() {
const _renderItem = ({item, index}) = > {
return (
<Text key={index}>{item}</Text>)}return (
<List
data={[1, 2.3.4.5.6]}
itemHeight={()= > 50}
renderItem={_renderItem}
/>)}Copy the code